Initial commit
This commit is contained in:
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": []
|
||||
}
|
||||
Vendored
+28
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"tasks": [
|
||||
{
|
||||
"type": "cppbuild",
|
||||
"label": "C/C++: gcc build active file",
|
||||
"command": "/usr/bin/gcc",
|
||||
"args": [
|
||||
"-fdiagnostics-color=always",
|
||||
"-g",
|
||||
"${file}",
|
||||
"-o",
|
||||
"${fileDirname}/${fileBasenameNoExtension}"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "${fileDirname}"
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$gcc"
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"detail": "Task generated by Debugger."
|
||||
}
|
||||
],
|
||||
"version": "2.0.0"
|
||||
}
|
||||
+965
@@ -0,0 +1,965 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <math.h>
|
||||
|
||||
#define CACHE_SIZE 4096
|
||||
#define AIR_ROUTE_HASH_SIZE 1024
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN STRUCTURES
|
||||
typedef struct Node { // Nodo puntato sia dalla hash table che dall'heap. Usato in Dijkstra
|
||||
int x, y; // Coordinate
|
||||
int cost; // Costo di raggiungimento
|
||||
int heap_index; // Indice nell'heap
|
||||
struct Node* next; // Puntatore al prossimo elemento (hash table con chaining)
|
||||
} Node;
|
||||
|
||||
typedef struct { // Hash table
|
||||
int size; // Quantità di nodi presenti
|
||||
int capacity; // Capacità massima
|
||||
Node** buckets; // Puntatore all'array di puntatori ai Node
|
||||
Node* pool; // Puntatore alla pool di memoria contigua
|
||||
int pool_index; // Indice del pool di memoria contigua
|
||||
} HashTable;
|
||||
|
||||
typedef struct MinHeap { // Heap
|
||||
int size; // Quantità di nodi presenti
|
||||
int capacity; // Capacità massima
|
||||
Node** queue; // Puntatore all'array di puntatori ai Node
|
||||
} MinHeap;
|
||||
|
||||
typedef struct { // Rotta aerea
|
||||
int dest_x, dest_y; // Destinazione
|
||||
int cost; // Costo di raggiungimento
|
||||
} AirRoute;
|
||||
|
||||
typedef struct AirRouteNode {
|
||||
int start_x, start_y; // Coordinate di partenza
|
||||
AirRoute routes[5]; // Array di rotte aeree
|
||||
int route_count; // Numero di rotte aeree
|
||||
struct AirRouteNode* next; // Puntatore al prossimo nodo nella hash table (chaining)
|
||||
} AirRouteNode;
|
||||
|
||||
typedef struct {
|
||||
int size; // Quantità di buckets
|
||||
int capacity; // Capacità massima di nodi
|
||||
AirRouteNode** buckets; // Puntatore all'array di puntatori ai AirRouteNode
|
||||
AirRouteNode* pool; // Puntatore alla pool di memoria contigua
|
||||
int pool_index; // Indice del pool di memoria contigua
|
||||
} AirRouteTable;
|
||||
|
||||
typedef struct { // Mappa di esagoni - UPDATED: no more Hexagon structure
|
||||
int rows, cols; // Numero di righe e colonne
|
||||
int* grid_data; // Puntatore al blocco in RAM contenente tutti i costi degli esagoni
|
||||
int** grid; // Array di puntatori alle colonne
|
||||
AirRouteTable* air_routes; // Puntatore alla hash table contenente gli air routes
|
||||
} HexMap;
|
||||
|
||||
typedef struct CacheNode { // Nodo della cache
|
||||
int xp, yp; // Coordinate di partenza
|
||||
int xd, yd; // Coordinate di arrivo
|
||||
int cost; // Costo di percorrenza
|
||||
struct CacheNode* next; // Puntatore al prossimo elemento (hash table chained)
|
||||
struct CacheNode* lru_prev; // Puntatore all'elemento precedente nella double linked list LRU
|
||||
struct CacheNode* lru_next; // Puntatore al prossimo elemento nella double linked list LRU
|
||||
} CacheNode;
|
||||
|
||||
typedef struct { // Hash table della cache
|
||||
int size; // Quantità di bucket
|
||||
int capacity; // Quantità massima di nodi prima di iniziare ad attuare la policy di LRU
|
||||
int element_number; // Quantità attuale di elementi nella cache
|
||||
CacheNode** buckets; // Puntatore all'array di puntatori ai CacheNode
|
||||
CacheNode* pool; // Puntatore alla pool di memoria contigua
|
||||
int pool_index; // Indice del pool di memoria contigua
|
||||
CacheNode* head; // CacheNode più recentemente usato
|
||||
CacheNode* tail; // CacheNode meno recentemente usato
|
||||
} CacheHashTable;
|
||||
// END STRUCTURES
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN AIR ROUTE TABLE FUNCTIONS
|
||||
inline int calculate_air_route_hash(int x, int y, int size) {
|
||||
// Calcola la hash nella hash table delle rotte aeree
|
||||
return ((x * 73 + y * 31) & (size - 1));
|
||||
}
|
||||
|
||||
inline AirRouteTable* create_air_route_table(int capacity) {
|
||||
// Allco la tabella di hash e inizializzo gli attributi
|
||||
AirRouteTable* table = (AirRouteTable*) malloc(sizeof(AirRouteTable));
|
||||
table->size = AIR_ROUTE_HASH_SIZE;
|
||||
table->capacity = capacity;
|
||||
table->buckets = (AirRouteNode**) calloc(AIR_ROUTE_HASH_SIZE, sizeof(AirRouteNode*));
|
||||
table->pool = (AirRouteNode*) malloc(capacity * sizeof(AirRouteNode));
|
||||
table->pool_index = 0;
|
||||
return table;
|
||||
}
|
||||
|
||||
inline void clear_air_route_table(AirRouteTable* table) {
|
||||
// Mette a 0 tutti i bucket e va a resettare il pool di memoria contigua
|
||||
// NOTA: non va a fare delle free perchè al prossimo utilizzo della hash table vado a sovrascrivere i nodi (verranno sempre scritti nel pool di memoria)
|
||||
memset(table->buckets, 0, table->size * sizeof(AirRouteNode*));
|
||||
table->pool_index = 0;
|
||||
}
|
||||
|
||||
void destroy_air_route_table(AirRouteTable** table) {
|
||||
// Elimina completamente una hash table delle rotte aeree (anche il suo pool di memoria contigua)
|
||||
if (*table) {
|
||||
free((*table)->buckets);
|
||||
free((*table)->pool);
|
||||
free(*table);
|
||||
*table = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
inline AirRouteNode* find_air_route_node(AirRouteTable* table, int x, int y) {
|
||||
// Trova il nodo contenente le rotte aeree date le coordinate dell'esagono di partenza
|
||||
int index = calculate_air_route_hash(x, y, table->size);
|
||||
AirRouteNode* current = table->buckets[index];
|
||||
|
||||
while (current != NULL) {
|
||||
if (current->start_x == x && current->start_y == y) {
|
||||
return current;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline void remove_air_route_node_if_empty(AirRouteTable* table, int x, int y) {
|
||||
// Quando un nodo di rotte aeree ha 0 rotte aeree nell'array, vado ad eliminare il nodo dalla hash table
|
||||
int index = calculate_air_route_hash(x, y, table->size);
|
||||
AirRouteNode* current = table->buckets[index];
|
||||
AirRouteNode* prev = NULL;
|
||||
|
||||
while (current != NULL) {
|
||||
if (current->start_x == x && current->start_y == y) {
|
||||
if (current->route_count == 0) {
|
||||
if (prev == NULL) {
|
||||
table->buckets[index] = current->next;
|
||||
} else {
|
||||
prev->next = current->next;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
prev = current;
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
|
||||
bool toggle_air_route_in_node(AirRouteTable* table, HexMap* map, int start_x, int start_y, int dest_x, int dest_y) {
|
||||
// Inserisce o rimuove una rotta aerea da un esagono
|
||||
// NOTA: Ritorna sempre true se va tutto bene (ritorna false invece se ci sono problemi)
|
||||
AirRouteNode* route_node = find_air_route_node(table, start_x, start_y);
|
||||
|
||||
// Controlla se la rotta aerea è già presente e, in caso, la rimuove
|
||||
if (route_node != NULL) {
|
||||
for (int i = 0; i < route_node->route_count; i++){
|
||||
if ((route_node->routes[i].dest_x == dest_x) && (route_node->routes[i].dest_y == dest_y)){
|
||||
// Rimuove la rotta
|
||||
for (int j = i; j < route_node->route_count - 1; j++){
|
||||
route_node->routes[j] = route_node->routes[j+1];
|
||||
}
|
||||
route_node->route_count--;
|
||||
|
||||
// Chiamo la funzione per controllare che ci siano ancora rotte aeree (in caso contrario viene eliminato il nodo)
|
||||
remove_air_route_node_if_empty(table, start_x, start_y);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Controlla se posso aggiungere rotte aeree
|
||||
if (route_node->route_count >= 5){
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Creo il nodo delle rotte aeree (se entro qua è perchè il nodo non esiste già)
|
||||
if (table->pool_index >= table->capacity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int index = calculate_air_route_hash(start_x, start_y, table->size);
|
||||
route_node = &table->pool[table->pool_index++];
|
||||
route_node->start_x = start_x;
|
||||
route_node->start_y = start_y;
|
||||
route_node->route_count = 0;
|
||||
route_node->next = table->buckets[index];
|
||||
table->buckets[index] = route_node;
|
||||
}
|
||||
|
||||
// Aggiorno il nodo delle rotte aeree (se sono qua è perchè esiste già il nodo)
|
||||
int starting_hex_cost = map->grid[start_x][start_y];
|
||||
int cost;
|
||||
if (route_node->route_count == 0){
|
||||
cost = (int) floor(starting_hex_cost / (route_node->route_count + 1));
|
||||
} else {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < route_node->route_count; i++){
|
||||
sum += route_node->routes[i].cost;
|
||||
}
|
||||
cost = (int) floor((sum + starting_hex_cost) / (route_node->route_count + 1));
|
||||
}
|
||||
|
||||
route_node->routes[route_node->route_count] = (AirRoute) {dest_x, dest_y, cost};
|
||||
route_node->route_count++;
|
||||
|
||||
return true;
|
||||
}
|
||||
// END AIR ROUTE TABLE FUNCTIONS
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN MAP FUNCTIONS
|
||||
inline void init_map(HexMap* map, int cols, int rows){
|
||||
// Inizializza la griglia: prende in input il puntatore alla struttura mappa, righe e colonne e crea la griglia formata da un array di puntatori ad array
|
||||
map->rows = rows;
|
||||
map->cols = cols;
|
||||
|
||||
// Alloca lo spazio di tutti i costi degli esagoni in blocco (per minimizzare cache miss)
|
||||
map->grid_data = (int*) calloc(cols * rows, sizeof(int));
|
||||
|
||||
// Alloca i puntatori alle singole colonne
|
||||
map->grid = (int**) malloc(cols * sizeof(int*));
|
||||
|
||||
// Inizializza i puntatori alle singole colonne
|
||||
for (int i = 0; i < cols; i++) {
|
||||
map->grid[i] = &map->grid_data[i * rows];
|
||||
}
|
||||
|
||||
// Inizializza i costi di tutti gli esagoni a 1
|
||||
for (int i = 0; i < cols * rows; i++) {
|
||||
map->grid_data[i] = 1;
|
||||
}
|
||||
|
||||
// Inizializza la hash map delle rotte aeree
|
||||
map->air_routes = create_air_route_table(cols * rows / 10); // Verranno aggiunte massimo un decimo di nodi di rotte aeree
|
||||
|
||||
fprintf(stdout,"OK\n");
|
||||
}
|
||||
|
||||
void destroy_map(HexMap* map){
|
||||
// Distrugge la mappa
|
||||
free(map->grid_data); // Dealloca il mega blocco contenente tutti gli esagoni
|
||||
free(map->grid); // Dealloca i singoli puntatori alle colonne
|
||||
destroy_air_route_table(&map->air_routes); // Dealloca la hash table di rotte aeree
|
||||
map->grid = NULL;
|
||||
map->grid_data = NULL;
|
||||
map->air_routes = NULL;
|
||||
map->rows = map->cols = 0;
|
||||
}
|
||||
|
||||
inline void print_map(HexMap* map){
|
||||
// Stampa la mappa
|
||||
printf("\n");
|
||||
for (int i=map->rows-1; i>=0; i--){
|
||||
if(i%2==1) printf(" ");
|
||||
for (int j=0; j<map->cols; j++){
|
||||
printf("%d ",map->grid[j][i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
inline bool is_valid(HexMap* map, int x, int y){
|
||||
// Controlla se delle coordinate sono entro i bordi della mappa
|
||||
return !(x<0 || x>map->cols-1 || y<0 || y>map->rows-1);
|
||||
}
|
||||
|
||||
inline int hexagons_distance(int x_1, int y_1, int x_2, int y_2){
|
||||
// Calcola la distanza tra due esagoni tramite le coordinate cubiche (non ho idea di come funzioni, fidati)
|
||||
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)));
|
||||
}
|
||||
|
||||
bool change_cost(HexMap* map, int x, int y, int cost, int radius){
|
||||
// Cambia il costo nella mappa: per ogni esagono nella mappa all'interno del quadrato che inscrive il cerchio di raggio radius calcola la distanza dal nodo sorgente, se è inferiore del raggio allora cambia il costo dell'esagono secondo la formula. Successivamente aggiorna i costi delle rotte aeree
|
||||
// NOTA: il costo può variare massimo tra 0 e 100
|
||||
if(!is_valid(map, x, y) || radius<=0 || cost < -10 || cost > 10){
|
||||
fputs("KO\n", stdout);
|
||||
return false;
|
||||
}
|
||||
|
||||
int dist;
|
||||
float coeff;
|
||||
|
||||
// Le x e le y che vado a modificare sono solo quelle dentro al quadrato che inscrive la circonferenza di raggio radius
|
||||
int min_x = fmax(0, x - radius);
|
||||
int max_x = fmin(map->cols - 1, x + radius);
|
||||
int min_y = fmax(0, y - radius);
|
||||
int max_y = fmin(map->rows - 1, y + radius);
|
||||
|
||||
for(int i = min_x; i <= max_x; i++){
|
||||
for (int j = min_y; j <= max_y; j++){
|
||||
dist = hexagons_distance(x, y, i, j);
|
||||
if (dist < radius){
|
||||
coeff = fmax(0.0f, (float)(radius - dist) / (float)radius);
|
||||
int* hex_cost = &map->grid[i][j];
|
||||
*hex_cost = *hex_cost + (int)floor(cost * coeff);
|
||||
|
||||
if (*hex_cost > 100){
|
||||
*hex_cost = 100;
|
||||
}
|
||||
if (*hex_cost < 0){
|
||||
*hex_cost = 0;
|
||||
}
|
||||
|
||||
// Aggiorna le rotte aeree (NON TOCCARE)
|
||||
AirRouteNode* route_node = find_air_route_node(map->air_routes, i, j);
|
||||
if (route_node != NULL) {
|
||||
for (int counter = 0; counter < route_node->route_count; counter++){
|
||||
int cost_old_air_route = 0;
|
||||
for (int n = 0; n < counter; n++){
|
||||
cost_old_air_route += route_node->routes[n].cost;
|
||||
}
|
||||
route_node->routes[counter].cost = (int)((cost_old_air_route + *hex_cost) / (counter + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fputs("OK\n", stdout);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool toggle_air_route(HexMap* map, int x_1, int y_1, int x_2, int y_2){
|
||||
// Inserisce o rimuove una rotta aerea da un esagono
|
||||
if(!is_valid(map, x_1,y_1) || !is_valid(map, x_2,y_2)){
|
||||
fputs("KO\n", stdout);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = toggle_air_route_in_node(map->air_routes, map, x_1, y_1, x_2, y_2);
|
||||
|
||||
if (success) {
|
||||
fputs("OK\n", stdout);
|
||||
return true;
|
||||
} else {
|
||||
fputs("KO\n", stdout);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// END MAP FUNCTIONS
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN HASH TABLE FUNCTIONS
|
||||
inline int calculate_hash(int x, int y, int size){
|
||||
// Calcola l'hash dati in input le coordinate e la dimensione della hash table
|
||||
return ((x * 73 + y * 31) & (size - 1));
|
||||
}
|
||||
|
||||
inline HashTable* create_hash_table(int size){
|
||||
// Genera l'hash table con fattore di carico 1.5 per bilanciare performance temporali e spaziali. Calcola il numero primo migliore da usare come dimensione dell'hash table e poi inizializza tutti i suoi attributi
|
||||
// NOTA: con calloc() vado a inizializzare già tutti i bucket a 0, quindi non devo preoccuparmi di farli puntare a NULL
|
||||
|
||||
int hash_length=65536;
|
||||
|
||||
HashTable* ht = (HashTable*) malloc(sizeof(HashTable));
|
||||
ht->size = hash_length;
|
||||
ht->capacity = size;
|
||||
ht->buckets = calloc(hash_length, sizeof(Node*));
|
||||
ht->pool = malloc(size * sizeof(Node));
|
||||
ht->pool_index = 0;
|
||||
|
||||
return ht;
|
||||
}
|
||||
|
||||
inline void clear_hash_table(HashTable* ht){
|
||||
// Mette a 0 tutti i bucket e va a resettare il pool di memoria contigua
|
||||
// NOTA: non va a fare delle free perchè al prossimo utilizzo della hash table vado a sovrascrivere i nodi (verranno sempre scritti nel pool di memoria)
|
||||
memset(ht->buckets, 0, ht->size * sizeof(Node*));
|
||||
ht->pool_index = 0;
|
||||
}
|
||||
|
||||
void destroy_hash_table(HashTable** ht){
|
||||
// Elimina completamente una hash table (anche il suo pool di memoria contigua)
|
||||
if (*ht) {
|
||||
free((*ht)->buckets);
|
||||
free((*ht)->pool);
|
||||
free(*ht);
|
||||
*ht = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
inline Node* insert_or_update_element(HashTable* ht, int x, int y, int cost){
|
||||
// Se il nodo che sto provando ad inserire non esiste, lo inserisco in testa alla chain.
|
||||
// Se invece esiste controllo il costo: se è maggiore di quello già presente in hash table ignoro (ritorno NULL), altrimenti aggiorno il costo
|
||||
// NOTA: quando inserisco per la prima volta in hash table inizializzo heap_index a -1 per intendere che non è ancora stato inserito in heap
|
||||
int index = calculate_hash(x, y, ht->size);
|
||||
|
||||
Node* current = ht->buckets[index];
|
||||
while (current!=NULL) {
|
||||
if (current->x == x && current->y == y) {
|
||||
if (current->cost > cost) {
|
||||
current->cost = cost;
|
||||
return current;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
if (ht->pool_index >= ht->capacity){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Node* new_node = &ht->pool[ht->pool_index++];
|
||||
new_node->x = x;
|
||||
new_node->y = y;
|
||||
new_node->cost = cost;
|
||||
new_node->heap_index = -1;
|
||||
new_node->next = ht->buckets[index];
|
||||
ht->buckets[index] = new_node;
|
||||
|
||||
return new_node;
|
||||
}
|
||||
// END HASH TABLE FUNCTIONS
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN HEAP FUNCTIONS
|
||||
inline void heapify_up(MinHeap* heap, int index) {
|
||||
// Fa salire un nodo dal basso verso l'alto (lo swappo con il genitore)
|
||||
// NOTA: viene usato quando inserisco un nuovo nodo: lo inserisco come foglia e poi lo faccio risalire fino alla sua posizione corretta
|
||||
Node** queue = heap->queue;
|
||||
int parent;
|
||||
int current_cost;
|
||||
int parent_cost;
|
||||
|
||||
while (index > 0) {
|
||||
parent = (index - 1) >> 1;
|
||||
|
||||
current_cost = queue[index]->cost;
|
||||
parent_cost = queue[parent]->cost;
|
||||
|
||||
if (current_cost >= parent_cost) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Swappa i nodi
|
||||
Node* temp = queue[index];
|
||||
queue[index] = queue[parent];
|
||||
queue[parent] = temp;
|
||||
|
||||
// Aggiorna gli indici
|
||||
queue[index]->heap_index = index;
|
||||
queue[parent]->heap_index = parent;
|
||||
|
||||
index = parent;
|
||||
}
|
||||
}
|
||||
|
||||
inline void heapify_down(MinHeap* heap, int index) {
|
||||
// Fa scendere un nodo dall'alto verso il basso (lo swappo con un figlio)
|
||||
// NOTA: viene usato quando consumo il nodo minimo (root) in Dijkstra: metto il nodo più grande di tutti come root e poi lo faccio scendere fino alla posizione corretta
|
||||
int left, right, smallest;
|
||||
const int size = heap->size;
|
||||
Node** queue = heap->queue;
|
||||
int current_cost;
|
||||
|
||||
while (true) {
|
||||
left = (index << 1) + 1;
|
||||
right = left + 1;
|
||||
smallest = index;
|
||||
|
||||
current_cost = queue[smallest]->cost;
|
||||
|
||||
// Figlio sinistro
|
||||
if (left < size) {
|
||||
int left_cost = queue[left]->cost;
|
||||
if (left_cost < current_cost) {
|
||||
smallest = left;
|
||||
current_cost = left_cost;
|
||||
}
|
||||
}
|
||||
|
||||
// Figlio destro
|
||||
if (right < size) {
|
||||
int right_cost = queue[right]->cost;
|
||||
if (right_cost < current_cost) {
|
||||
smallest = right;
|
||||
}
|
||||
}
|
||||
|
||||
if (smallest == index) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Swap dei nodi
|
||||
Node* temp = queue[index];
|
||||
queue[index] = queue[smallest];
|
||||
queue[smallest] = temp;
|
||||
|
||||
// Aggiorna gli indici
|
||||
queue[index]->heap_index = index;
|
||||
queue[smallest]->heap_index = smallest;
|
||||
|
||||
index = smallest;
|
||||
}
|
||||
}
|
||||
|
||||
inline void heap_enqueue(MinHeap* heap, Node* node){
|
||||
// Se il nodo non è già presente in heap (index==-1) allora lo aggiungo come foglia e poi lo faccio risalire.
|
||||
// Se invece il nodo esiste già, gli aggiorno il costo e poi lo faccio risalire (il nuovo costo è per forza minore, quindi deve salire)
|
||||
// NOTA: sono sicuro che il costo nuovo sia inferiore del precedente perchè chiamo l'heap_enqueue solo dopo aver controllato tramite la hash table
|
||||
if (node->heap_index == -1) {
|
||||
if(heap->size >= heap->capacity){
|
||||
return;
|
||||
}
|
||||
|
||||
node->heap_index = heap->size;
|
||||
heap->queue[heap->size] = node;
|
||||
heap->size++;
|
||||
|
||||
heapify_up(heap, node->heap_index);
|
||||
} else {
|
||||
heapify_up(heap, node->heap_index);
|
||||
}
|
||||
}
|
||||
|
||||
inline Node* heap_dequeue(MinHeap* heap){
|
||||
// Consumo il primo nodo della heap: gli imposto l'index a -1 per intendere che non è più in heap e poi lo ritorno
|
||||
// NOTA: per sistemare l'heap metto come root il nodo più grande (quello all'ultimo indice) in root e poi lo faccio scendere
|
||||
if (heap->size == 0){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Node* min = heap->queue[0];
|
||||
min->heap_index = -1;
|
||||
|
||||
heap->size--;
|
||||
|
||||
if (heap->size > 0) {
|
||||
heap->queue[0] = heap->queue[heap->size];
|
||||
heap->queue[0]->heap_index = 0;
|
||||
heapify_down(heap, 0);
|
||||
}
|
||||
|
||||
return min;
|
||||
}
|
||||
|
||||
inline MinHeap* heap_create(int capacity){
|
||||
// Crea l'heap
|
||||
MinHeap* heap = malloc(sizeof(MinHeap));
|
||||
heap->queue = malloc(sizeof(Node*) * capacity);
|
||||
heap->size = 0;
|
||||
heap->capacity = capacity;
|
||||
return heap;
|
||||
}
|
||||
|
||||
inline void heap_clear(MinHeap* heap){
|
||||
// Imposta semplicemente la dimensione dell'heap a 0, tanto ai successivi usi vado a sovrascrivere i puntatori che sono presenti
|
||||
heap->size = 0;
|
||||
}
|
||||
|
||||
void heap_destroy(MinHeap* heap){
|
||||
// Vado ad eliminare completamente l'heap
|
||||
free(heap->queue);
|
||||
free(heap);
|
||||
}
|
||||
// END HEAP FUNCTIONS
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN DIJKSTRA
|
||||
int travel_cost(HexMap* map, HashTable* ht, MinHeap* heap, int xp, int yp, int xd, int yd) {
|
||||
// Inserisco il nodo sorgente in hash table e in heap.
|
||||
// Nel while (che va avanti finchè non esaurisco i nodi nell'heap) faccio:
|
||||
// - Prendo il primo elemento dall'heap (quindi il minimo)
|
||||
// - Se è il nodo destinazione, ritorno il costo di raggiungimento (non mi serve esplorare ulteriormente per come è fatto Dijkstra, appena lo trovo ho già trovato il costo minore)
|
||||
// - Controllo i 6 nodi vicini e, se il loro costo di raggiungimento è minore di quello che già hanno in hash table (oppure se vengono inseriti per la prima volta in hash table), vado ad inserirli anche in heap
|
||||
// - Controllo tutte le rotte aeree del nodo e, se il nodo di destinazione ha costo di raggiungimento minore di quello già presente in hash table, lo inserisco in heap
|
||||
// Controllo alla fine il costo di raggiungimento del nodo di destinazione (teoricamente non dovrei mai arrivarci qui)
|
||||
// Pulisco heap e hash table
|
||||
|
||||
// Controllo la validità delle coordinate
|
||||
if ((unsigned)xp >= map->cols || (unsigned)yp >= map->rows || (unsigned)xd >= map->cols || (unsigned)yd >= map->rows) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Esco se partenza e destinazione coincidono
|
||||
if (xp == xd && yp == yd){
|
||||
return 0;
|
||||
}
|
||||
|
||||
Node* new_node = insert_or_update_element(ht, xp, yp, 0);
|
||||
heap_enqueue(heap, new_node);
|
||||
|
||||
const int cols = map->cols;
|
||||
const int rows = map->rows;
|
||||
|
||||
static const int dir_even[6][2] = {
|
||||
{+1, 0}, { 0, -1}, {+1, -1},
|
||||
{-1, 0}, {+1, +1}, { 0, +1}
|
||||
};
|
||||
static const int dir_odd[6][2] = {
|
||||
{+1, 0}, {-1, -1}, { 0, -1},
|
||||
{-1, 0}, { 0, +1}, {-1, +1}
|
||||
};
|
||||
|
||||
while (heap->size > 0) {
|
||||
Node* current = heap_dequeue(heap);
|
||||
|
||||
// Se current==destinazione
|
||||
if (current->x == xd && current->y == yd) {
|
||||
int result = current->cost;
|
||||
heap_clear(heap);
|
||||
clear_hash_table(ht);
|
||||
return result;
|
||||
}
|
||||
|
||||
const int land_cost = map->grid[current->x][current->y];
|
||||
|
||||
if (land_cost == 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
// Scelgo i vicini in base alla parità
|
||||
const int (*dirs)[2] = (current->y & 1) ? dir_even : dir_odd;
|
||||
int new_cost = current->cost + land_cost;
|
||||
|
||||
// Controllo i 6 vicini
|
||||
for (int i = 0; i < 6; i++) {
|
||||
int new_x = current->x + dirs[i][0];
|
||||
int new_y = current->y + dirs[i][1];
|
||||
|
||||
if ((unsigned)new_x < cols && (unsigned)new_y < rows) {
|
||||
new_node = insert_or_update_element(ht, new_x, new_y, new_cost);
|
||||
if (new_node) {
|
||||
heap_enqueue(heap, new_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Controllo le rotte aeree
|
||||
AirRouteNode* route_node = find_air_route_node(map->air_routes, current->x, current->y);
|
||||
if (route_node != NULL) {
|
||||
for (int i = 0; i < route_node->route_count; i++) {
|
||||
new_node = insert_or_update_element(ht,route_node->routes[i].dest_x, route_node->routes[i].dest_y, current->cost + route_node->routes[i].cost);
|
||||
if (new_node) {
|
||||
heap_enqueue(heap, new_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Destinazione non raggiunta
|
||||
heap_clear(heap);
|
||||
clear_hash_table(ht);
|
||||
return -1;
|
||||
}
|
||||
// END DIJKSTRA
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN CACHE FUNCTIONS
|
||||
inline int calculate_cache_hash(int xp, int yp, int xd, int yd, int size){
|
||||
// Calcola l'hash dati in input le quattro coordinate e la dimensione della hash table della cache
|
||||
return ((xp * 19 + yp * 13 + xd * 27 + yd * 121) & (size-1));
|
||||
}
|
||||
|
||||
inline CacheHashTable* create_cache(int capacity){
|
||||
// Crea la cache con un fattore di carico di circa 1.2 (per massimizzare efficienza temporale e spaziale).
|
||||
// Inizializza tutti gli attributi della tabella hash stessa (dimensione, capacità, pool ecc...) e e anche della lista doppiamente concatenata LRU (head e tail)
|
||||
|
||||
int hash_length = CACHE_SIZE;
|
||||
|
||||
// Hash table
|
||||
CacheHashTable* cache = (CacheHashTable*)malloc(sizeof(CacheHashTable));
|
||||
cache->size = hash_length;
|
||||
cache->capacity = capacity;
|
||||
cache->element_number = 0;
|
||||
cache->buckets = calloc(hash_length, sizeof(CacheNode*));
|
||||
cache->pool = malloc(capacity * sizeof(CacheNode));
|
||||
cache->pool_index = 0;
|
||||
|
||||
// Lista LRU
|
||||
cache->head = &cache->pool[cache->pool_index++];
|
||||
cache->tail = &cache->pool[cache->pool_index++];
|
||||
cache->head->lru_next = cache->tail;
|
||||
cache->tail->lru_prev = cache->head;
|
||||
cache->head->lru_prev = NULL;
|
||||
cache->tail->lru_next = NULL;
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
inline void lru_move_to_head(CacheHashTable* cache, CacheNode* node){
|
||||
// Prende in ingresso un nodo già presente in lista e lo inserisce in testa (quando faccio una lookup deve andare in testa)
|
||||
|
||||
// Gestisce i puntatori prima di spostare il nodo
|
||||
if (node->lru_prev){
|
||||
node->lru_prev->lru_next = node->lru_next;
|
||||
}
|
||||
if (node->lru_next){
|
||||
node->lru_next->lru_prev = node->lru_prev;
|
||||
}
|
||||
|
||||
// Sposta il nodo in testa
|
||||
node->lru_next = cache->head->lru_next;
|
||||
node->lru_prev = cache->head;
|
||||
cache->head->lru_next->lru_prev = node;
|
||||
cache->head->lru_next = node;
|
||||
}
|
||||
|
||||
inline void remove_node(CacheHashTable* cache, CacheNode* node, int xp, int yp, int xd, int yd){
|
||||
// Rimuove un nodo sia dalla lista che dalla hash table
|
||||
|
||||
// Rimozione dalla hash table
|
||||
int index = calculate_cache_hash(xp, yp, xd, yd, cache->size);
|
||||
CacheNode* current = cache->buckets[index];
|
||||
CacheNode* prev = NULL;
|
||||
|
||||
while (current != NULL) {
|
||||
if (current == node) {
|
||||
if (prev == NULL) {
|
||||
cache->buckets[index] = current->next;
|
||||
} else {
|
||||
prev->next = current->next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
prev = current;
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
// Rimozione dalla lista LRU
|
||||
if (node->lru_prev) node->lru_prev->lru_next = node->lru_next;
|
||||
if (node->lru_next) node->lru_next->lru_prev = node->lru_prev;
|
||||
|
||||
cache->element_number--;
|
||||
}
|
||||
|
||||
inline CacheNode* cache_lookup(CacheHashTable* cache, int xp, int yp, int xd, int yd){
|
||||
// Cerca un nodo in cache e, se lo trova, lo muove in testa nella lista LRU
|
||||
int index = calculate_cache_hash(xp, yp, xd, yd, cache->size);
|
||||
CacheNode* current = cache->buckets[index];
|
||||
|
||||
while (current != NULL) {
|
||||
if (current->xp == xp && current->yp == yp && current->xd == xd && current->yd == yd) { // Se viene trovato il nodo
|
||||
lru_move_to_head(cache, current);
|
||||
return current;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
return NULL; // Se non viene trovato, restituisco NULL
|
||||
}
|
||||
|
||||
inline CacheNode* cache_insert(CacheHashTable* cache, int xp, int yp, int xd, int yd, int cost){
|
||||
// Cerca nell'hash table se il nodo esiste già (nella lookup c'è già lo spostamento in testa). Nel caso aggiorna il costo
|
||||
CacheNode* existing = cache_lookup(cache, xp, yp, xd, yd);
|
||||
if (existing != NULL) {
|
||||
existing->cost = cost;
|
||||
return existing;
|
||||
}
|
||||
|
||||
// Se la cache è piena, rimuovo il nodo in coda
|
||||
if (cache->element_number >= cache->capacity) {
|
||||
CacheNode* lru_node = cache->tail->lru_prev;
|
||||
if (lru_node != cache->head) {
|
||||
remove_node(cache, lru_node, lru_node->xp, lru_node->yp, lru_node->xd, lru_node->yd);
|
||||
}
|
||||
}
|
||||
|
||||
// Arrivo qua solamente se il nodo non è già presente in cache; creo il nodo
|
||||
CacheNode* new_node = &cache->pool[cache->pool_index++];
|
||||
new_node->xp = xp;
|
||||
new_node->yp = yp;
|
||||
new_node->xd = xd;
|
||||
new_node->yd = yd;
|
||||
new_node->cost = cost;
|
||||
|
||||
// Lo aggiungo in hash table
|
||||
int index = calculate_cache_hash(xp, yp, xd, yd, cache->size);
|
||||
new_node->next = cache->buckets[index];
|
||||
cache->buckets[index] = new_node;
|
||||
|
||||
// Lo aggiungo in testa alla coda
|
||||
new_node->lru_next = cache->head->lru_next;
|
||||
new_node->lru_prev = cache->head;
|
||||
cache->head->lru_next->lru_prev = new_node;
|
||||
cache->head->lru_next = new_node;
|
||||
|
||||
cache->element_number++;
|
||||
return new_node;
|
||||
}
|
||||
|
||||
inline void cache_remove(CacheHashTable* cache, int xp, int yp, int xd, int yd){
|
||||
// Trova un nodo in cache e lo rimuove sia dalla hash table che dalla lista LRU
|
||||
int index = calculate_cache_hash(xp, yp, xd, yd, cache->size);
|
||||
CacheNode* current = cache->buckets[index];
|
||||
|
||||
while (current != NULL) {
|
||||
if (current->xp == xp && current->yp == yp && current->xd == xd && current->yd == yd) {
|
||||
remove_node(cache, current, xp, yp, xd, yd); // Rimuove sia dalla hash table che dalla lista LRU
|
||||
return;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
|
||||
void clear_cache(CacheHashTable* cache){
|
||||
// Pulisce tutta la cache ma ne mantiene la struttura (per non dover reinizializzare ogni volta la hash table)
|
||||
// NOTA: non faccio la free sugli elementi perchè sono tutti nella pool: verranno sovrascritti dopo
|
||||
memset(cache->buckets, 0, cache->size * sizeof(CacheNode*));
|
||||
cache->pool_index = 0;
|
||||
cache->element_number = 0;
|
||||
|
||||
// Resetta head e tail
|
||||
cache->head = &cache->pool[cache->pool_index++];
|
||||
cache->tail = &cache->pool[cache->pool_index++];
|
||||
cache->head->lru_next = cache->tail;
|
||||
cache->tail->lru_prev = cache->head;
|
||||
cache->head->lru_prev = NULL;
|
||||
cache->tail->lru_next = NULL;
|
||||
}
|
||||
|
||||
void destroy_cache(CacheHashTable** cache){
|
||||
// Elimino completamente la struttura e ripulisco la pool
|
||||
if (*cache) {
|
||||
free((*cache)->buckets);
|
||||
free((*cache)->pool);
|
||||
free(*cache);
|
||||
*cache = NULL;
|
||||
}
|
||||
}
|
||||
// END CACHE FUNCTIONS
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN MAIN
|
||||
int main(){
|
||||
char testo[17];
|
||||
int inp_uno, inp_due, inp_tre, inp_quattro;
|
||||
|
||||
HexMap map;
|
||||
HashTable* ht = NULL;
|
||||
MinHeap* heap = NULL;
|
||||
CacheHashTable* cache = NULL;
|
||||
|
||||
int already_initialized=0;
|
||||
CacheNode* cached;
|
||||
int cost;
|
||||
|
||||
while (true){
|
||||
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 (heap!=NULL){
|
||||
heap_destroy(heap);
|
||||
}
|
||||
heap = heap_create(inp_uno * inp_due);
|
||||
|
||||
if (cache!=NULL){
|
||||
destroy_cache(&cache);
|
||||
}
|
||||
cache = create_cache(CACHE_SIZE);
|
||||
}
|
||||
|
||||
if (strcmp(testo, "print")==0){
|
||||
if (already_initialized==0){
|
||||
fprintf(stdout, "-1\n");
|
||||
continue;
|
||||
}
|
||||
print_map(&map);
|
||||
}
|
||||
|
||||
if (strcmp(testo, "change_cost")==0){
|
||||
if (already_initialized==0){
|
||||
fprintf(stdout, "-1\n");
|
||||
continue;
|
||||
}
|
||||
if(change_cost(&map, inp_uno, inp_due, inp_tre, inp_quattro)){
|
||||
clear_cache(cache);
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(testo, "toggle_air_route")==0){
|
||||
if (already_initialized==0){
|
||||
fprintf(stdout, "-1\n");
|
||||
continue;
|
||||
}
|
||||
if(toggle_air_route(&map, inp_uno, inp_due, inp_tre, inp_quattro)){
|
||||
clear_cache(cache);
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(testo, "travel_cost")==0){
|
||||
if (already_initialized==0){
|
||||
fprintf(stdout, "-1\n");
|
||||
continue;
|
||||
}
|
||||
cached=cache_lookup(cache, inp_uno, inp_due, inp_tre, inp_quattro);
|
||||
|
||||
if(cached!=NULL){
|
||||
cost = cached->cost;
|
||||
} else {
|
||||
cost = travel_cost(&map, ht, heap, inp_uno, inp_due, inp_tre, inp_quattro);
|
||||
cache_insert(cache, inp_uno, inp_due, inp_tre, inp_quattro, cost);
|
||||
}
|
||||
|
||||
fprintf(stdout, "%d\n", cost);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
if (already_initialized==1) {
|
||||
destroy_map(&map);
|
||||
}
|
||||
if (ht!=NULL) {
|
||||
destroy_hash_table(&ht);
|
||||
}
|
||||
if (heap!=NULL) {
|
||||
heap_destroy(heap);
|
||||
}
|
||||
if (cache!=NULL) {
|
||||
destroy_cache(&cache);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
// END
|
||||
@@ -0,0 +1,941 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <math.h>
|
||||
|
||||
#define CACHE_SIZE 4096
|
||||
#define AIR_ROUTE_HASH_SIZE 1024
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Strutture dati per l'implementazione
|
||||
typedef struct Node { // elemento per hash e heap
|
||||
int x, y; // coordinate cella
|
||||
int costo; // costo percorso
|
||||
int pos_heap; // posizione nell'heap
|
||||
struct Node* prossimo; // puntatore chain hash
|
||||
} Node;
|
||||
|
||||
typedef struct { // Tabella hash
|
||||
int num_elementi; // elementi inseriti
|
||||
int max_size; // massimo numero elementi
|
||||
Node** bucket; // array bucket
|
||||
Node* memoria_pool; // pool allocazione
|
||||
int idx_pool; // indice corrente pool
|
||||
} HashTable;
|
||||
|
||||
typedef struct MinHeap { // coda priorità minima
|
||||
int num_elem; // elementi presenti
|
||||
int capienza; // capienza massima
|
||||
Node** coda; // array puntatori nodi
|
||||
} MinHeap;
|
||||
|
||||
typedef struct { // collegamento aereo
|
||||
int dest_x, dest_y; // destinazione volo
|
||||
int prezzo; // costo volo
|
||||
} AirRoute;
|
||||
|
||||
typedef struct AirRouteNode {
|
||||
int parte_x, parte_y; // partenza
|
||||
AirRoute voli[5]; // voli disponibili
|
||||
int num_voli; // numero voli
|
||||
struct AirRouteNode* succ; // prossimo nella hash
|
||||
} AirRouteNode;
|
||||
|
||||
typedef struct {
|
||||
int dimensione; // num bucket
|
||||
int massimo; // max elementi
|
||||
AirRouteNode** contenitori; // bucket array
|
||||
AirRouteNode* pool_mem; // pool memoria
|
||||
int indice_pool; // indice pool
|
||||
} AirRouteTable;
|
||||
|
||||
typedef struct { // Mappa esagonale
|
||||
int righe, colonne; // dimensioni griglia
|
||||
int* dati_griglia; // blocco dati costi
|
||||
int** griglia; // puntatori colonne
|
||||
AirRouteTable* rotte_aeree; // tabella rotte
|
||||
} HexMap;
|
||||
|
||||
typedef struct CacheNode { // nodo cache
|
||||
int xp, yp; // partenza
|
||||
int xa, ya; // arrivo
|
||||
int costo_path; // costo percorso
|
||||
struct CacheNode* next_chain; // prossimo hash chain
|
||||
struct CacheNode* precedente; // precedente in lista LRU
|
||||
struct CacheNode* seguente; // seguente in lista LRU
|
||||
} CacheNode;
|
||||
|
||||
typedef struct { // Cache con LRU
|
||||
int dim_hash; // dimensione hash
|
||||
int max_elementi; // massimo prima di LRU
|
||||
int conta_elem; // elementi attuali
|
||||
CacheNode** buckets; // array bucket
|
||||
CacheNode* pool_cache; // pool allocazione
|
||||
int idx_pool_cache; // indice pool
|
||||
CacheNode* primo; // più recente
|
||||
CacheNode* ultimo; // meno recente
|
||||
} CacheHashTable;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Funzioni per gestione rotte aeree
|
||||
inline int hash_rotta(int x, int y, int size) {
|
||||
// hash per tabella rotte - funziona abbastanza bene
|
||||
return ((x * 73 + y * 31) & (size - 1));
|
||||
}
|
||||
|
||||
inline AirRouteTable* crea_tabella_rotte(int capacity) {
|
||||
// alloca e inizializza tabella rotte aeree
|
||||
AirRouteTable* tabella = (AirRouteTable*) malloc(sizeof(AirRouteTable));
|
||||
tabella->dimensione = AIR_ROUTE_HASH_SIZE;
|
||||
tabella->massimo = capacity;
|
||||
tabella->contenitori = (AirRouteNode**) calloc(AIR_ROUTE_HASH_SIZE, sizeof(AirRouteNode*));
|
||||
tabella->pool_mem = (AirRouteNode*) malloc(capacity * sizeof(AirRouteNode));
|
||||
tabella->indice_pool = 0;
|
||||
return tabella;
|
||||
}
|
||||
|
||||
inline void pulisci_tabella_rotte(AirRouteTable* tabella) {
|
||||
// resetta tutti i bucket e riazzera il pool
|
||||
// non faccio free perché tanto sovrascrivo dopo
|
||||
memset(tabella->contenitori, 0, tabella->dimensione * sizeof(AirRouteNode*));
|
||||
tabella->indice_pool = 0;
|
||||
}
|
||||
|
||||
void distruggi_tabella_rotte(AirRouteTable** tabella) {
|
||||
// libera memoria tabella rotte
|
||||
if (*tabella) {
|
||||
free((*tabella)->contenitori);
|
||||
free((*tabella)->pool_mem);
|
||||
free(*tabella);
|
||||
*tabella = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
inline AirRouteNode* trova_nodo_rotte(AirRouteTable* tabella, int x, int y) {
|
||||
// cerca nodo con rotte per coordinate date
|
||||
int indice = hash_rotta(x, y, tabella->dimensione);
|
||||
AirRouteNode* attuale = tabella->contenitori[indice];
|
||||
|
||||
while (attuale != NULL) {
|
||||
if (attuale->parte_x == x && attuale->parte_y == y) {
|
||||
return attuale;
|
||||
}
|
||||
attuale = attuale->succ;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline void rimuovi_nodo_se_vuoto(AirRouteTable* tabella, int x, int y) {
|
||||
// se nodo non ha più rotte lo elimino dalla hash
|
||||
int indice = hash_rotta(x, y, tabella->dimensione);
|
||||
AirRouteNode* corrente = tabella->contenitori[indice];
|
||||
AirRouteNode* prec = NULL;
|
||||
|
||||
while (corrente != NULL) {
|
||||
if (corrente->parte_x == x && corrente->parte_y == y) {
|
||||
if (corrente->num_voli == 0) {
|
||||
if (prec == NULL) {
|
||||
tabella->contenitori[indice] = corrente->succ;
|
||||
} else {
|
||||
prec->succ = corrente->succ;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
prec = corrente;
|
||||
corrente = corrente->succ;
|
||||
}
|
||||
}
|
||||
|
||||
bool modifica_rotta_aerea(AirRouteTable* tabella, HexMap* mappa, int start_x, int start_y, int dest_x, int dest_y) {
|
||||
// aggiunge o rimuove rotta aerea - toggle
|
||||
AirRouteNode* nodo_rotte = trova_nodo_rotte(tabella, start_x, start_y);
|
||||
|
||||
// controllo se rotta già esiste per rimuoverla
|
||||
if (nodo_rotte != NULL) {
|
||||
for (int i = 0; i < nodo_rotte->num_voli; i++){
|
||||
if ((nodo_rotte->voli[i].dest_x == dest_x) && (nodo_rotte->voli[i].dest_y == dest_y)){
|
||||
// rimuovo questa rotta
|
||||
for (int j = i; j < nodo_rotte->num_voli - 1; j++){
|
||||
nodo_rotte->voli[j] = nodo_rotte->voli[j+1];
|
||||
}
|
||||
nodo_rotte->num_voli--;
|
||||
|
||||
// controllo se devo eliminare il nodo
|
||||
rimuovi_nodo_se_vuoto(tabella, start_x, start_y);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// controllo se posso aggiungere altre rotte
|
||||
if (nodo_rotte->num_voli >= 5){
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// devo creare nuovo nodo rotte
|
||||
if (tabella->indice_pool >= tabella->massimo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int indice = hash_rotta(start_x, start_y, tabella->dimensione);
|
||||
nodo_rotte = &tabella->pool_mem[tabella->indice_pool++];
|
||||
nodo_rotte->parte_x = start_x;
|
||||
nodo_rotte->parte_y = start_y;
|
||||
nodo_rotte->num_voli = 0;
|
||||
nodo_rotte->succ = tabella->contenitori[indice];
|
||||
tabella->contenitori[indice] = nodo_rotte;
|
||||
}
|
||||
|
||||
// aggiungo la nuova rotta al nodo
|
||||
int costo_hex = mappa->griglia[start_x][start_y];
|
||||
int prezzo_volo;
|
||||
if (nodo_rotte->num_voli == 0){
|
||||
prezzo_volo = (int) floor(costo_hex / (nodo_rotte->num_voli + 1));
|
||||
} else {
|
||||
int somma = 0;
|
||||
for (int i = 0; i < nodo_rotte->num_voli; i++){
|
||||
somma += nodo_rotte->voli[i].prezzo;
|
||||
}
|
||||
prezzo_volo = (int) floor((somma + costo_hex) / (nodo_rotte->num_voli + 1));
|
||||
}
|
||||
|
||||
nodo_rotte->voli[nodo_rotte->num_voli] = (AirRoute) {dest_x, dest_y, prezzo_volo};
|
||||
nodo_rotte->num_voli++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Funzioni mappa
|
||||
inline void inizializza_mappa(HexMap* mappa, int cols, int rows){
|
||||
// setup iniziale della griglia esagonale
|
||||
mappa->righe = rows;
|
||||
mappa->colonne = cols;
|
||||
|
||||
// alloco tutto insieme per località memoria
|
||||
mappa->dati_griglia = (int*) calloc(cols * rows, sizeof(int));
|
||||
|
||||
// array puntatori alle colonne
|
||||
mappa->griglia = (int**) malloc(cols * sizeof(int*));
|
||||
|
||||
// imposto puntatori colonne
|
||||
for (int i = 0; i < cols; i++) {
|
||||
mappa->griglia[i] = &mappa->dati_griglia[i * rows];
|
||||
}
|
||||
|
||||
// inizializzo tutti costi a 1
|
||||
for (int i = 0; i < cols * rows; i++) {
|
||||
mappa->dati_griglia[i] = 1;
|
||||
}
|
||||
|
||||
// creo tabella rotte aeree
|
||||
mappa->rotte_aeree = crea_tabella_rotte(cols * rows / 10); // circa 10% celle con rotte
|
||||
|
||||
fprintf(stdout,"OK\n");
|
||||
}
|
||||
|
||||
void distruggi_mappa(HexMap* mappa){
|
||||
// cleanup completo mappa
|
||||
free(mappa->dati_griglia); // libero blocco principale
|
||||
free(mappa->griglia); // libero puntatori colonne
|
||||
distruggi_tabella_rotte(&mappa->rotte_aeree); // libero rotte aeree
|
||||
mappa->griglia = NULL;
|
||||
mappa->dati_griglia = NULL;
|
||||
mappa->rotte_aeree = NULL;
|
||||
mappa->righe = mappa->colonne = 0;
|
||||
}
|
||||
|
||||
inline void stampa_mappa(HexMap* mappa){
|
||||
// output mappa per debug
|
||||
printf("\n");
|
||||
for (int i=mappa->righe-1; i>=0; i--){
|
||||
if(i%2==1) printf(" ");
|
||||
for (int j=0; j<mappa->colonne; j++){
|
||||
printf("%d ",mappa->griglia[j][i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
inline bool coordinate_valide(HexMap* mappa, int x, int y){
|
||||
// check bounds mappa
|
||||
return !(x<0 || x>mappa->colonne-1 || y<0 || y>mappa->righe-1);
|
||||
}
|
||||
|
||||
inline int distanza_esagoni(int x_1, int y_1, int x_2, int y_2){
|
||||
// calcolo distanza tra esagoni - formula presa da internet, funziona
|
||||
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)));
|
||||
}
|
||||
|
||||
bool cambia_costo(HexMap* mappa, int x, int y, int costo, int raggio){
|
||||
// modifica costi in area circolare attorno a punto
|
||||
if(!coordinate_valide(mappa, x, y) || raggio<=0 || costo < -10 || costo > 10){
|
||||
fputs("KO\n", stdout);
|
||||
return false;
|
||||
}
|
||||
|
||||
int dist;
|
||||
float coefficiente;
|
||||
|
||||
// limito area da controllare al quadrato che contiene il cerchio
|
||||
int min_x = fmax(0, x - raggio);
|
||||
int max_x = fmin(mappa->colonne - 1, x + raggio);
|
||||
int min_y = fmax(0, y - raggio);
|
||||
int max_y = fmin(mappa->righe - 1, y + raggio);
|
||||
|
||||
for(int i = min_x; i <= max_x; i++){
|
||||
for (int j = min_y; j <= max_y; j++){
|
||||
dist = distanza_esagoni(x, y, i, j);
|
||||
if (dist < raggio){
|
||||
coefficiente = fmax(0.0f, (float)(raggio - dist) / (float)raggio);
|
||||
int* costo_hex = &mappa->griglia[i][j];
|
||||
*costo_hex = *costo_hex + (int)floor(costo * coefficiente);
|
||||
|
||||
if (*costo_hex > 100){
|
||||
*costo_hex = 100;
|
||||
}
|
||||
if (*costo_hex < 0){
|
||||
*costo_hex = 0;
|
||||
}
|
||||
|
||||
// aggiorno rotte aeree se presenti
|
||||
AirRouteNode* nodo_rotte = trova_nodo_rotte(mappa->rotte_aeree, i, j);
|
||||
if (nodo_rotte != NULL) {
|
||||
for (int cnt = 0; cnt < nodo_rotte->num_voli; cnt++){
|
||||
int costo_vecchi_voli = 0;
|
||||
for (int n = 0; n < cnt; n++){
|
||||
costo_vecchi_voli += nodo_rotte->voli[n].prezzo;
|
||||
}
|
||||
nodo_rotte->voli[cnt].prezzo = (int)((costo_vecchi_voli + *costo_hex) / (cnt + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fputs("OK\n", stdout);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool toggle_rotta_aerea(HexMap* mappa, int x_1, int y_1, int x_2, int y_2){
|
||||
// attiva/disattiva collegamento aereo
|
||||
if(!coordinate_valide(mappa, x_1,y_1) || !coordinate_valide(mappa, x_2,y_2)){
|
||||
fputs("KO\n", stdout);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool esito = modifica_rotta_aerea(mappa->rotte_aeree, mappa, x_1, y_1, x_2, y_2);
|
||||
|
||||
if (esito) {
|
||||
fputs("OK\n", stdout);
|
||||
return true;
|
||||
} else {
|
||||
fputs("KO\n", stdout);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Funzioni hash table per dijkstra
|
||||
inline int calcola_hash(int x, int y, int size){
|
||||
// funzione hash per coordinate - numeri scelti a caso che funzionano bene
|
||||
return ((x * 73 + y * 31) & (size - 1));
|
||||
}
|
||||
|
||||
inline HashTable* crea_hash_table(int size){
|
||||
// alloca hash table per dijkstra con pool memoria
|
||||
|
||||
int lung_hash=65536;
|
||||
|
||||
HashTable* ht = (HashTable*) malloc(sizeof(HashTable));
|
||||
ht->num_elementi = lung_hash;
|
||||
ht->max_size = size;
|
||||
ht->bucket = calloc(lung_hash, sizeof(Node*));
|
||||
ht->memoria_pool = malloc(size * sizeof(Node));
|
||||
ht->idx_pool = 0;
|
||||
|
||||
return ht;
|
||||
}
|
||||
|
||||
inline void pulisci_hash_table(HashTable* ht){
|
||||
// reset hash table senza deallocare - riuso pool
|
||||
memset(ht->bucket, 0, ht->num_elementi * sizeof(Node*));
|
||||
ht->idx_pool = 0;
|
||||
}
|
||||
|
||||
void distruggi_hash_table(HashTable** ht){
|
||||
// deallocazione completa hash table
|
||||
if (*ht) {
|
||||
free((*ht)->bucket);
|
||||
free((*ht)->memoria_pool);
|
||||
free(*ht);
|
||||
*ht = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
inline Node* inserisci_o_aggiorna(HashTable* ht, int x, int y, int costo){
|
||||
// inserisce nuovo nodo o aggiorna esistente se costo minore
|
||||
// ritorna NULL se non serve aggiornare
|
||||
int indice = calcola_hash(x, y, ht->num_elementi);
|
||||
|
||||
Node* attuale = ht->bucket[indice];
|
||||
while (attuale!=NULL) {
|
||||
if (attuale->x == x && attuale->y == y) {
|
||||
if (attuale->costo > costo) {
|
||||
attuale->costo = costo;
|
||||
return attuale;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
attuale = attuale->prossimo;
|
||||
}
|
||||
|
||||
if (ht->idx_pool >= ht->max_size){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Node* nuovo_nodo = &ht->memoria_pool[ht->idx_pool++];
|
||||
nuovo_nodo->x = x;
|
||||
nuovo_nodo->y = y;
|
||||
nuovo_nodo->costo = costo;
|
||||
nuovo_nodo->pos_heap = -1;
|
||||
nuovo_nodo->prossimo = ht->bucket[indice];
|
||||
ht->bucket[indice] = nuovo_nodo;
|
||||
|
||||
return nuovo_nodo;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Funzioni heap minimo
|
||||
inline void risali_heap(MinHeap* heap, int indice) {
|
||||
// fa risalire elemento verso radice se ha costo minore del padre
|
||||
Node** coda = heap->coda;
|
||||
int padre;
|
||||
int costo_attuale;
|
||||
int costo_padre;
|
||||
|
||||
while (indice > 0) {
|
||||
padre = (indice - 1) >> 1;
|
||||
|
||||
costo_attuale = coda[indice]->costo;
|
||||
costo_padre = coda[padre]->costo;
|
||||
|
||||
if (costo_attuale >= costo_padre) {
|
||||
break;
|
||||
}
|
||||
|
||||
// swap nodi
|
||||
Node* temp = coda[indice];
|
||||
coda[indice] = coda[padre];
|
||||
coda[padre] = temp;
|
||||
|
||||
// aggiorno indici
|
||||
coda[indice]->pos_heap = indice;
|
||||
coda[padre]->pos_heap = padre;
|
||||
|
||||
indice = padre;
|
||||
}
|
||||
}
|
||||
|
||||
inline void scendi_heap(MinHeap* heap, int indice) {
|
||||
// fa scendere elemento verso foglie se ha costo maggiore dei figli
|
||||
int sinistro, destro, minimo;
|
||||
const int dim = heap->num_elem;
|
||||
Node** coda = heap->coda;
|
||||
int costo_attuale;
|
||||
|
||||
while (true) {
|
||||
sinistro = (indice << 1) + 1;
|
||||
destro = sinistro + 1;
|
||||
minimo = indice;
|
||||
|
||||
costo_attuale = coda[minimo]->costo;
|
||||
|
||||
// controllo figlio sinistro
|
||||
if (sinistro < dim) {
|
||||
int costo_sin = coda[sinistro]->costo;
|
||||
if (costo_sin < costo_attuale) {
|
||||
minimo = sinistro;
|
||||
costo_attuale = costo_sin;
|
||||
}
|
||||
}
|
||||
|
||||
// controllo figlio destro
|
||||
if (destro < dim) {
|
||||
int costo_des = coda[destro]->costo;
|
||||
if (costo_des < costo_attuale) {
|
||||
minimo = destro;
|
||||
}
|
||||
}
|
||||
|
||||
if (minimo == indice) {
|
||||
break;
|
||||
}
|
||||
|
||||
// swap elementi
|
||||
Node* temp = coda[indice];
|
||||
coda[indice] = coda[minimo];
|
||||
coda[minimo] = temp;
|
||||
|
||||
// aggiorno posizioni
|
||||
coda[indice]->pos_heap = indice;
|
||||
coda[minimo]->pos_heap = minimo;
|
||||
|
||||
indice = minimo;
|
||||
}
|
||||
}
|
||||
|
||||
inline void inserisci_in_heap(MinHeap* heap, Node* nodo){
|
||||
// inserisce nodo in heap o aggiorna posizione se già presente
|
||||
if (nodo->pos_heap == -1) {
|
||||
if(heap->num_elem >= heap->capienza){
|
||||
return;
|
||||
}
|
||||
|
||||
nodo->pos_heap = heap->num_elem;
|
||||
heap->coda[heap->num_elem] = nodo;
|
||||
heap->num_elem++;
|
||||
|
||||
risali_heap(heap, nodo->pos_heap);
|
||||
} else {
|
||||
risali_heap(heap, nodo->pos_heap);
|
||||
}
|
||||
}
|
||||
|
||||
inline Node* estrai_minimo_heap(MinHeap* heap){
|
||||
// prende elemento con costo minimo e riordina heap
|
||||
if (heap->num_elem == 0){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Node* minimo = heap->coda[0];
|
||||
minimo->pos_heap = -1;
|
||||
|
||||
heap->num_elem--;
|
||||
|
||||
if (heap->num_elem > 0) {
|
||||
heap->coda[0] = heap->coda[heap->num_elem];
|
||||
heap->coda[0]->pos_heap = 0;
|
||||
scendi_heap(heap, 0);
|
||||
}
|
||||
|
||||
return minimo;
|
||||
}
|
||||
|
||||
inline MinHeap* crea_heap(int capacità){
|
||||
// alloca heap minimo
|
||||
MinHeap* heap = malloc(sizeof(MinHeap));
|
||||
heap->coda = malloc(sizeof(Node*) * capacità);
|
||||
heap->num_elem = 0;
|
||||
heap->capienza = capacità;
|
||||
return heap;
|
||||
}
|
||||
|
||||
inline void pulisci_heap(MinHeap* heap){
|
||||
// resetta heap senza deallocare
|
||||
heap->num_elem = 0;
|
||||
}
|
||||
|
||||
void distruggi_heap(MinHeap* heap){
|
||||
// dealloca completamente heap
|
||||
free(heap->coda);
|
||||
free(heap);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Algoritmo dijkstra
|
||||
int calcola_costo_viaggio(HexMap* mappa, HashTable* ht, MinHeap* heap, int xp, int yp, int xd, int yd) {
|
||||
// dijkstra per trovare percorso minimo tra due punti
|
||||
// uso heap per prendere sempre nodo con costo minore
|
||||
// uso hash per non rivisitare nodi già processati
|
||||
|
||||
// controllo coordinate
|
||||
if ((unsigned)xp >= mappa->colonne || (unsigned)yp >= mappa->righe || (unsigned)xd >= mappa->colonne || (unsigned)yd >= mappa->righe) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// se partenza e arrivo sono uguali
|
||||
if (xp == xd && yp == yd){
|
||||
return 0;
|
||||
}
|
||||
|
||||
Node* nuovo_nodo = inserisci_o_aggiorna(ht, xp, yp, 0);
|
||||
inserisci_in_heap(heap, nuovo_nodo);
|
||||
|
||||
const int cols = mappa->colonne;
|
||||
const int rows = mappa->righe;
|
||||
|
||||
// direzioni esagono per righe pari e dispari - pattern diverso
|
||||
static const int dir_pari[6][2] = {
|
||||
{+1, 0}, { 0, -1}, {+1, -1},
|
||||
{-1, 0}, {+1, +1}, { 0, +1}
|
||||
};
|
||||
static const int dir_dispari[6][2] = {
|
||||
{+1, 0}, {-1, -1}, { 0, -1},
|
||||
{-1, 0}, { 0, +1}, {-1, +1}
|
||||
};
|
||||
|
||||
while (heap->num_elem > 0) {
|
||||
Node* attuale = estrai_minimo_heap(heap);
|
||||
|
||||
// se ho raggiunto destinazione
|
||||
if (attuale->x == xd && attuale->y == yd) {
|
||||
int risultato = attuale->costo;
|
||||
pulisci_heap(heap);
|
||||
pulisci_hash_table(ht);
|
||||
return risultato;
|
||||
}
|
||||
|
||||
const int costo_terreno = mappa->griglia[attuale->x][attuale->y];
|
||||
|
||||
if (costo_terreno == 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
// scelgo direzioni in base se riga pari o dispari
|
||||
const int (*direzioni)[2] = (attuale->y & 1) ? dir_pari : dir_dispari;
|
||||
int nuovo_costo = attuale->costo + costo_terreno;
|
||||
|
||||
// controllo tutti i 6 vicini
|
||||
for (int i = 0; i < 6; i++) {
|
||||
int nuova_x = attuale->x + direzioni[i][0];
|
||||
int nuova_y = attuale->y + direzioni[i][1];
|
||||
|
||||
if ((unsigned)nuova_x < cols && (unsigned)nuova_y < rows) {
|
||||
nuovo_nodo = inserisci_o_aggiorna(ht, nuova_x, nuova_y, nuovo_costo);
|
||||
if (nuovo_nodo) {
|
||||
inserisci_in_heap(heap, nuovo_nodo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// controllo se ci sono rotte aeree da questa posizione
|
||||
AirRouteNode* nodo_rotte = trova_nodo_rotte(mappa->rotte_aeree, attuale->x, attuale->y);
|
||||
if (nodo_rotte != NULL) {
|
||||
for (int i = 0; i < nodo_rotte->num_voli; i++) {
|
||||
nuovo_nodo = inserisci_o_aggiorna(ht, nodo_rotte->voli[i].dest_x, nodo_rotte->voli[i].dest_y, attuale->costo + nodo_rotte->voli[i].prezzo);
|
||||
if (nuovo_nodo) {
|
||||
inserisci_in_heap(heap, nuovo_nodo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// destinazione non raggiungibile
|
||||
pulisci_heap(heap);
|
||||
pulisci_hash_table(ht);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Funzioni cache LRU
|
||||
inline int hash_cache(int xp, int yp, int xd, int yd, int size){
|
||||
// hash per cache - combina tutte e 4 coordinate
|
||||
return ((xp * 19 + yp * 13 + xd * 27 + yd * 121) & (size-1));
|
||||
}
|
||||
|
||||
inline CacheHashTable* crea_cache(int capacity){
|
||||
// alloca cache con LRU policy
|
||||
|
||||
int lung_hash = CACHE_SIZE;
|
||||
|
||||
// struttura principale
|
||||
CacheHashTable* cache = (CacheHashTable*)malloc(sizeof(CacheHashTable));
|
||||
cache->dim_hash = lung_hash;
|
||||
cache->max_elementi = capacity;
|
||||
cache->conta_elem = 0;
|
||||
cache->buckets = calloc(lung_hash, sizeof(CacheNode*));
|
||||
cache->pool_cache = malloc(capacity * sizeof(CacheNode));
|
||||
cache->idx_pool_cache = 0;
|
||||
|
||||
// lista doppia LRU con sentinelle
|
||||
cache->primo = &cache->pool_cache[cache->idx_pool_cache++];
|
||||
cache->ultimo = &cache->pool_cache[cache->idx_pool_cache++];
|
||||
cache->primo->seguente = cache->ultimo;
|
||||
cache->ultimo->precedente = cache->primo;
|
||||
cache->primo->precedente = NULL;
|
||||
cache->ultimo->seguente = NULL;
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
inline void sposta_in_testa(CacheHashTable* cache, CacheNode* nodo){
|
||||
// sposta nodo in testa alla lista LRU (più recente)
|
||||
|
||||
// scollego nodo dalla posizione attuale
|
||||
if (nodo->precedente){
|
||||
nodo->precedente->seguente = nodo->seguente;
|
||||
}
|
||||
if (nodo->seguente){
|
||||
nodo->seguente->precedente = nodo->precedente;
|
||||
}
|
||||
|
||||
// inserisco in testa
|
||||
nodo->seguente = cache->primo->seguente;
|
||||
nodo->precedente = cache->primo;
|
||||
cache->primo->seguente->precedente = nodo;
|
||||
cache->primo->seguente = nodo;
|
||||
}
|
||||
|
||||
inline void rimuovi_nodo_cache(CacheHashTable* cache, CacheNode* nodo, int xp, int yp, int xd, int yd){
|
||||
// elimina nodo da hash e da lista LRU
|
||||
|
||||
// rimozione da hash table
|
||||
int indice = hash_cache(xp, yp, xd, yd, cache->dim_hash);
|
||||
CacheNode* attuale = cache->buckets[indice];
|
||||
CacheNode* prec = NULL;
|
||||
|
||||
while (attuale != NULL) {
|
||||
if (attuale == nodo) {
|
||||
if (prec == NULL) {
|
||||
cache->buckets[indice] = attuale->next_chain;
|
||||
} else {
|
||||
prec->next_chain = attuale->next_chain;
|
||||
}
|
||||
break;
|
||||
}
|
||||
prec = attuale;
|
||||
attuale = attuale->next_chain;
|
||||
}
|
||||
|
||||
// rimozione da lista LRU
|
||||
if (nodo->precedente) nodo->precedente->seguente = nodo->seguente;
|
||||
if (nodo->seguente) nodo->seguente->precedente = nodo->precedente;
|
||||
|
||||
cache->conta_elem--;
|
||||
}
|
||||
|
||||
inline CacheNode* cerca_in_cache(CacheHashTable* cache, int xp, int yp, int xd, int yd){
|
||||
// cerca percorso in cache e sposta in testa se trovato
|
||||
int indice = hash_cache(xp, yp, xd, yd, cache->dim_hash);
|
||||
CacheNode* attuale = cache->buckets[indice];
|
||||
|
||||
while (attuale != NULL) {
|
||||
if (attuale->xp == xp && attuale->yp == yp && attuale->xa == xd && attuale->ya == yd) { // trovato
|
||||
sposta_in_testa(cache, attuale);
|
||||
return attuale;
|
||||
}
|
||||
attuale = attuale->next_chain;
|
||||
}
|
||||
return NULL; // non trovato
|
||||
}
|
||||
|
||||
inline CacheNode* inserisci_in_cache(CacheHashTable* cache, int xp, int yp, int xd, int yd, int costo){
|
||||
// inserisce percorso in cache o aggiorna se già presente
|
||||
CacheNode* esistente = cerca_in_cache(cache, xp, yp, xd, yd);
|
||||
if (esistente != NULL) {
|
||||
esistente->costo_path = costo;
|
||||
return esistente;
|
||||
}
|
||||
|
||||
// se cache piena rimuovo elemento meno recente
|
||||
if (cache->conta_elem >= cache->max_elementi) {
|
||||
CacheNode* nodo_lru = cache->ultimo->precedente;
|
||||
if (nodo_lru != cache->primo) {
|
||||
rimuovi_nodo_cache(cache, nodo_lru, nodo_lru->xp, nodo_lru->yp, nodo_lru->xa, nodo_lru->ya);
|
||||
}
|
||||
}
|
||||
|
||||
// creo nuovo nodo
|
||||
CacheNode* nuovo_nodo = &cache->pool_cache[cache->idx_pool_cache++];
|
||||
nuovo_nodo->xp = xp;
|
||||
nuovo_nodo->yp = yp;
|
||||
nuovo_nodo->xa = xd;
|
||||
nuovo_nodo->ya = yd;
|
||||
nuovo_nodo->costo_path = costo;
|
||||
|
||||
// inserisco in hash table
|
||||
int indice = hash_cache(xp, yp, xd, yd, cache->dim_hash);
|
||||
nuovo_nodo->next_chain = cache->buckets[indice];
|
||||
cache->buckets[indice] = nuovo_nodo;
|
||||
|
||||
// inserisco in testa lista LRU
|
||||
nuovo_nodo->seguente = cache->primo->seguente;
|
||||
nuovo_nodo->precedente = cache->primo;
|
||||
cache->primo->seguente->precedente = nuovo_nodo;
|
||||
cache->primo->seguente = nuovo_nodo;
|
||||
|
||||
cache->conta_elem++;
|
||||
return nuovo_nodo;
|
||||
}
|
||||
|
||||
inline void elimina_da_cache(CacheHashTable* cache, int xp, int yp, int xd, int yd){
|
||||
// trova e rimuove elemento specifico dalla cache
|
||||
int indice = hash_cache(xp, yp, xd, yd, cache->dim_hash);
|
||||
CacheNode* attuale = cache->buckets[indice];
|
||||
|
||||
while (attuale != NULL) {
|
||||
if (attuale->xp == xp && attuale->yp == yp && attuale->xa == xd && attuale->ya == yd) {
|
||||
rimuovi_nodo_cache(cache, attuale, xp, yp, xd, yd);
|
||||
return;
|
||||
}
|
||||
attuale = attuale->next_chain;
|
||||
}
|
||||
}
|
||||
|
||||
void svuota_cache(CacheHashTable* cache){
|
||||
// reset completo cache mantenendo struttura
|
||||
memset(cache->buckets, 0, cache->dim_hash * sizeof(CacheNode*));
|
||||
cache->idx_pool_cache = 0;
|
||||
cache->conta_elem = 0;
|
||||
|
||||
// ricrea sentinelle LRU
|
||||
cache->primo = &cache->pool_cache[cache->idx_pool_cache++];
|
||||
cache->ultimo = &cache->pool_cache[cache->idx_pool_cache++];
|
||||
cache->primo->seguente = cache->ultimo;
|
||||
cache->ultimo->precedente = cache->primo;
|
||||
cache->primo->precedente = NULL;
|
||||
cache->ultimo->seguente = NULL;
|
||||
}
|
||||
|
||||
void distruggi_cache(CacheHashTable** cache){
|
||||
// deallocazione completa cache
|
||||
if (*cache) {
|
||||
free((*cache)->buckets);
|
||||
free((*cache)->pool_cache);
|
||||
free(*cache);
|
||||
*cache = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Funzione principale
|
||||
int main(){
|
||||
char comando[17];
|
||||
int param1, param2, param3, param4;
|
||||
|
||||
HexMap mappa;
|
||||
HashTable* hash_dijkstra = NULL;
|
||||
MinHeap* heap_dijkstra = NULL;
|
||||
CacheHashTable* cache_percorsi = NULL;
|
||||
|
||||
int mappa_inizializzata=0;
|
||||
CacheNode* risultato_cache;
|
||||
int costo_percorso;
|
||||
|
||||
while (true){
|
||||
int esito_input = scanf("%s %d %d %d %d",comando, ¶m1, ¶m2, ¶m3, ¶m4);
|
||||
if (esito_input==-1){
|
||||
break;
|
||||
}
|
||||
|
||||
if (strcmp(comando, "init")==0){
|
||||
if (mappa_inizializzata==1){
|
||||
distruggi_mappa(&mappa);
|
||||
}
|
||||
inizializza_mappa(&mappa, param1, param2);
|
||||
mappa_inizializzata=1;
|
||||
|
||||
if (hash_dijkstra!=NULL){
|
||||
distruggi_hash_table(&hash_dijkstra);
|
||||
}
|
||||
hash_dijkstra = crea_hash_table(param1 * param2);
|
||||
|
||||
if (heap_dijkstra!=NULL){
|
||||
distruggi_heap(heap_dijkstra);
|
||||
}
|
||||
heap_dijkstra = crea_heap(param1 * param2);
|
||||
|
||||
if (cache_percorsi!=NULL){
|
||||
distruggi_cache(&cache_percorsi);
|
||||
}
|
||||
cache_percorsi = crea_cache(CACHE_SIZE);
|
||||
}
|
||||
|
||||
if (strcmp(comando, "print")==0){
|
||||
if (mappa_inizializzata==0){
|
||||
fprintf(stdout, "-1\n");
|
||||
continue;
|
||||
}
|
||||
stampa_mappa(&mappa);
|
||||
}
|
||||
|
||||
if (strcmp(comando, "change_cost")==0){
|
||||
if (mappa_inizializzata==0){
|
||||
fprintf(stdout, "-1\n");
|
||||
continue;
|
||||
}
|
||||
if(cambia_costo(&mappa, param1, param2, param3, param4)){
|
||||
svuota_cache(cache_percorsi);
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(comando, "toggle_air_route")==0){
|
||||
if (mappa_inizializzata==0){
|
||||
fprintf(stdout, "-1\n");
|
||||
continue;
|
||||
}
|
||||
if(toggle_rotta_aerea(&mappa, param1, param2, param3, param4)){
|
||||
svuota_cache(cache_percorsi);
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(comando, "travel_cost")==0){
|
||||
if (mappa_inizializzata==0){
|
||||
fprintf(stdout, "-1\n");
|
||||
continue;
|
||||
}
|
||||
risultato_cache=cerca_in_cache(cache_percorsi, param1, param2, param3, param4);
|
||||
|
||||
if(risultato_cache!=NULL){
|
||||
costo_percorso = risultato_cache->costo_path;
|
||||
} else {
|
||||
costo_percorso = calcola_costo_viaggio(&mappa, hash_dijkstra, heap_dijkstra, param1, param2, param3, param4);
|
||||
inserisci_in_cache(cache_percorsi, param1, param2, param3, param4, costo_percorso);
|
||||
}
|
||||
|
||||
fprintf(stdout, "%d\n", costo_percorso);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup finale
|
||||
if (mappa_inizializzata==1) {
|
||||
distruggi_mappa(&mappa);
|
||||
}
|
||||
if (hash_dijkstra!=NULL) {
|
||||
distruggi_hash_table(&hash_dijkstra);
|
||||
}
|
||||
if (heap_dijkstra!=NULL) {
|
||||
distruggi_heap(heap_dijkstra);
|
||||
}
|
||||
if (cache_percorsi!=NULL) {
|
||||
distruggi_cache(&cache_percorsi);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,722 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <math.h>
|
||||
|
||||
#define CACHE_SIZE 1024
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 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 la distanza tra due esagoni (tramite coordinate cubiche)
|
||||
|
||||
|
||||
// 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))); // Ritorno la distanza tra i due esagoni
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
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 || cost < -10 || cost > 10){
|
||||
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 dist;
|
||||
int cost_old_air_route;
|
||||
float coeff;
|
||||
for(int i=0; i<map->cols; i++){
|
||||
for (int j=0; j<map->rows; j++){
|
||||
dist=hexagons_distance(x,y,i,j);
|
||||
if (dist<radius){
|
||||
if(((float)(radius - dist) / (float)radius)>0){ // Calcolo del coefficiente (fidati)
|
||||
coeff=((float)(radius - dist) / (float)radius);
|
||||
} else {
|
||||
coeff=0;
|
||||
}
|
||||
|
||||
map->grid[i][j].land_cost=map->grid[i][j].land_cost+(int)floor(cost * coeff); // 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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;
|
||||
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]-> ", current -> x, current -> y, current -> distance_from_start);
|
||||
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){
|
||||
// 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
|
||||
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 -> 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int search_element (HashTable* ht, int x, int y){
|
||||
// Cerca un nodo dalla hash table e ne restituisce il costo
|
||||
|
||||
|
||||
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 il nodo non è presente, restituisce -1
|
||||
return -1;
|
||||
}
|
||||
|
||||
return current->distance_from_start;
|
||||
}
|
||||
|
||||
|
||||
// END
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN PATH-FINDING (Dijkstra)
|
||||
|
||||
|
||||
// BEGIN STRUTTURE
|
||||
|
||||
typedef struct {
|
||||
int x,y;
|
||||
int distance_from_start;
|
||||
} QueueNode_PathFinding;
|
||||
|
||||
// END
|
||||
|
||||
|
||||
void enqueue(QueueNode_PathFinding* queue, HashTable* ht, int x, int y, int node_cost, int head, int tail){
|
||||
// Controllo il costo del nodo x,y in hash table e se il valore che trovo è minore di quello che gli passo, non aggiungo in coda (non vale la pena aggiungere il percorso con un costo maggiore)
|
||||
int hn_cost = search_element(ht, x, y);
|
||||
if (node_cost >= hn_cost){
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int temp_cost;
|
||||
for (int i=head; i<tail; i++) {
|
||||
temp_cost = queue[i].distance_from_start; // Trovo il costo del nodo i-esimo nella coda
|
||||
|
||||
if(node_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, node_cost}; // 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
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int travel_cost(HexMap* map, HashTable* ht, int xp, int yp, int xd, int yd){
|
||||
// FUNZIONAMENTO:
|
||||
// È un algoritmo Dijkstra in cui tengo sempre una coda ORDINATA che ha in testa le coordinate x,y del nodo con minore costo, mentre in coda quello con maggior costo.
|
||||
// Il costo è dato dalla distanza dal nodo sorgente
|
||||
//
|
||||
// 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;
|
||||
}
|
||||
|
||||
// TODO realloc dinamica della coda
|
||||
int queue_dimension = map->rows * map->cols * sizeof(QueueNode_PathFinding)*100; // Inizializzo la coda con dimensione 100 x del numero di nodi, head e tail
|
||||
QueueNode_PathFinding* queue = malloc(queue_dimension);
|
||||
int head=0;
|
||||
int tail=0;
|
||||
|
||||
|
||||
queue[0] = (QueueNode_PathFinding) {xp, yp, 0}; // Inserisco il nodo sorgente in coda
|
||||
tail++;
|
||||
insert_or_update_element(ht, xp, yp, 0); // 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)
|
||||
int current_cost; // Il costo di current in coda
|
||||
int current_ht_cost; // Inizializzo current_ht_cost, ossia il costo del 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
|
||||
current_cost=current.distance_from_start; // Definisco il costo di current
|
||||
head++;
|
||||
current_ht_cost = search_element(ht, current.x, current.y); // Cerco current nella hash table e il costo corrispondente lo metto in current_ht_cost (NOTA: current sarà sicuramente nell'hash table)
|
||||
|
||||
|
||||
// Se il nodo che sto ispezionando non è quello con il costo dell'hash table, vado alla prossima iterazione del qhile (quello con costo minore o è già stato ispezionato oppure è in hash table)
|
||||
if (current.distance_from_start > current_ht_cost){
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ISPEZIONI I VICINI
|
||||
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}
|
||||
};
|
||||
|
||||
|
||||
// Scelgo la direzione 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 vicino 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_cost+map->grid[current.x][current.y].land_cost)){ // Se inserendolo ottengo true
|
||||
enqueue(queue, ht, new_x, new_y, current_cost, 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_cost+air_routes[i].cost)){ // Se inserendolo ottengo true
|
||||
enqueue(queue, ht, new_x, new_y, current_cost, head, tail); // Lo aggiungo alla coda
|
||||
tail++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int ris = search_element(ht, xd, yd);
|
||||
free(queue); // Dealloco la coda
|
||||
clear_hash_table(ht); // Pulisco la hash table
|
||||
return ris;
|
||||
}
|
||||
|
||||
|
||||
// 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){
|
||||
|
||||
// MAPPA
|
||||
if (already_initialized==1){
|
||||
destroy_map(&map);
|
||||
}
|
||||
init_map(&map, inp_uno, inp_due);
|
||||
already_initialized=1;
|
||||
|
||||
// HASH TABLE
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,811 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <math.h>
|
||||
|
||||
#define CACHE_SIZE 1024
|
||||
#define CACHE_MAX_OCCUPIED 4096
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN STRUCTURES
|
||||
typedef struct Node { // Nodo puntato sia dalla hash table che dall'heap. Usato in Dijkstra
|
||||
int x, y; // Coordinate
|
||||
int cost; // Costo di raggiungimento
|
||||
int heap_index; // Indice nell'heap
|
||||
struct Node* next; // Puntatore al prossimo elemento (hash table con chaining)
|
||||
} Node;
|
||||
|
||||
typedef struct { // Hash table
|
||||
int size; // Quantità di nodi presenti
|
||||
int capacity; // Capacità massima
|
||||
Node** buckets; // Puntatore all'array di puntatori ai Node
|
||||
Node* node_pool; // Puntatore alla pool di memoria contigua
|
||||
int pool_index; // Indice del pool di memoria contigua
|
||||
} HashTable;
|
||||
|
||||
typedef struct MinHeap { // Heap
|
||||
int size; // Quantità di nodi presenti
|
||||
int capacity; // Capacità massima
|
||||
Node** queue; // Puntatore all'array di puntatori ai Node
|
||||
} MinHeap;
|
||||
|
||||
typedef struct { // Rotta aerea
|
||||
int dest_x, dest_y; // Destinazione
|
||||
int cost; // Costo di raggiungimento
|
||||
} AirRoute;
|
||||
|
||||
typedef struct { // Esagono
|
||||
int land_cost; // Costo uscita esagono
|
||||
AirRoute air_routes[5]; // Array di rotte aeree
|
||||
int air_route_count; // Quantità di rotte aeree presenti
|
||||
} Hexagon;
|
||||
|
||||
typedef struct { // Mappa di esagoni
|
||||
int rows, cols; // Numero di righe e colonne
|
||||
Hexagon** grid; // Matrice vera e propria
|
||||
} HexMap;
|
||||
|
||||
|
||||
// CACHE DA SISTEMARE
|
||||
typedef struct CACHE_HashNode CACHE_HashNode;
|
||||
|
||||
struct CACHE_HashNode{
|
||||
int xp,yp;
|
||||
int xd,yd;
|
||||
int travel_cost;
|
||||
int LRU;
|
||||
CACHE_HashNode* next;
|
||||
};
|
||||
|
||||
typedef struct{
|
||||
int size;
|
||||
int occupied;
|
||||
CACHE_HashNode** buckets;
|
||||
} CACHE_HashTable;
|
||||
// END
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN MAP FUNCTIONS
|
||||
void init_map(HexMap* map, int cols, int rows){
|
||||
// Inizializza la griglia: prende in input il puntatore alla struttura mappa, righe e colonne e crea la griglia formata da un array di puntatori ad array
|
||||
map->rows = rows;
|
||||
map->cols = cols;
|
||||
|
||||
map->grid = (Hexagon**) malloc(cols * sizeof(Hexagon*));
|
||||
|
||||
for (int i=0; i<map->cols; i++){
|
||||
map->grid[i] = (Hexagon*) malloc(rows * sizeof(Hexagon));
|
||||
|
||||
for (int j=0; j<map->rows; j++){
|
||||
map->grid[i][j].land_cost=1;
|
||||
map->grid[i][j].air_route_count = 0;
|
||||
}
|
||||
}
|
||||
fprintf(stdout,"OK\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void destroy_map(HexMap* map){
|
||||
// Distrugge 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){
|
||||
// Stampa la mappa
|
||||
printf("\n");
|
||||
for (int i=map->rows-1; i>=0; i--){
|
||||
if(i%2==1) printf(" ");
|
||||
for (int j=0; j<map->cols; j++){
|
||||
printf("%d ",map->grid[j][i].land_cost);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool is_valid(HexMap* map, int x, int y){
|
||||
// Controlla se delle coordinate sono entro i bordi della mappa
|
||||
return !(x<0 || x>map->cols-1 || y<0 || y>map->rows-1);
|
||||
}
|
||||
|
||||
int hexagons_distance(int x_1, int y_1, int x_2, int y_2){
|
||||
// Calcola la distanza tra due esagoni tramite le coordinate cubiche
|
||||
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)));
|
||||
}
|
||||
|
||||
bool change_cost(HexMap* map, int x, int y, int cost, int radius){
|
||||
// Cambia il costo nella mappa: per ogni esagono nella mappa calcola la distanza dal nodo sorgente, se è inferiore del raggio allora cambia il costo dell'esagono secondo la formula. Successivamente aggiorna i costi delle rotte aeree
|
||||
// NOTA: il costo può variare massimo tra 0 e 100
|
||||
if(!is_valid(map, x, y) || radius<=0 || cost < -10 || cost > 10){
|
||||
fputs("KO\n", stdout);
|
||||
fflush(stdout);
|
||||
return false;
|
||||
}
|
||||
|
||||
int dist;
|
||||
int cost_old_air_route;
|
||||
float coeff;
|
||||
for(int i=0; i<map->cols; i++){
|
||||
for (int j=0; j<map->rows; j++){
|
||||
dist=hexagons_distance(x,y,i,j);
|
||||
if (dist<radius){
|
||||
if(((float)(radius - dist) / (float)radius)>0){
|
||||
coeff=((float)(radius - dist) / (float)radius);
|
||||
} else {
|
||||
coeff=0;
|
||||
}
|
||||
|
||||
map->grid[i][j].land_cost=map->grid[i][j].land_cost+(int)floor(cost * coeff);
|
||||
|
||||
if (map->grid[i][j].land_cost>100){
|
||||
map->grid[i][j].land_cost=100;
|
||||
}
|
||||
if (map->grid[i][j].land_cost<0){
|
||||
map->grid[i][j].land_cost=0;
|
||||
}
|
||||
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool toggle_air_route(HexMap* map, int x_1, int y_1, int x_2, int y_2){
|
||||
// Inserisce o rimuove una rotta aerea da un esagono
|
||||
if(!is_valid(map, x_1,y_1) || !is_valid(map, x_2,y_2)){
|
||||
fputs("KO\n", stdout);
|
||||
fflush(stdout);
|
||||
return false;
|
||||
}
|
||||
|
||||
Hexagon* StartingHexagon = &(map->grid[x_1][y_1]);
|
||||
|
||||
if (StartingHexagon->air_route_count>4){
|
||||
fputs("KO\n", stdout);
|
||||
fflush(stdout);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i=0; i< StartingHexagon->air_route_count; i++){
|
||||
if ((StartingHexagon->air_routes[i].dest_x == x_2)&&(StartingHexagon->air_routes[i].dest_y == y_2)){
|
||||
for (int j=i; j<StartingHexagon->air_route_count; j++){
|
||||
StartingHexagon->air_routes[j]=StartingHexagon->air_routes[j+1];
|
||||
}
|
||||
StartingHexagon->air_route_count = StartingHexagon->air_route_count - 1;
|
||||
fputs("OK\n", stdout);
|
||||
fflush(stdout);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
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};
|
||||
StartingHexagon->air_route_count = StartingHexagon->air_route_count + 1;
|
||||
fputs("OK\n", stdout);
|
||||
fflush(stdout);
|
||||
return true;
|
||||
}
|
||||
// END
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN HASH-TABLE FUNCTIONS
|
||||
static inline int calculate_hash(int x, int y, int size) {
|
||||
// Calcola l'hash date in input le coordinate e la dimensione della hash table
|
||||
unsigned int hash = 2166136261u;
|
||||
hash ^= (unsigned int)x;
|
||||
hash *= 16777619u;
|
||||
hash ^= (unsigned int)y;
|
||||
hash *= 16777619u;
|
||||
return hash % size;
|
||||
}
|
||||
|
||||
HashTable* create_hash_table(int size) {
|
||||
// Genera l'hash table con fattore di carico 1.2 per bilanciare performance temporali e spaziali. Calcola il numero primo migliore da usare come dimensione dell'hash table e poi inizializza tutti i suoi attributi
|
||||
// NOTA: con calloc() vado a inizializzare già tutti i bucket a 0, quindi non devo preoccuparmi di farli puntare a NULL
|
||||
int min_size = (int)ceil(size / 1.2);
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
int hash_length = 53;
|
||||
for (int i = 0; i < 22; i++) {
|
||||
if (primes[i] >= min_size) {
|
||||
hash_length = primes[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
HashTable* ht = (HashTable*) malloc(sizeof(HashTable));
|
||||
ht->size = hash_length;
|
||||
ht->capacity = size;
|
||||
ht->buckets = calloc(hash_length, sizeof(Node*));
|
||||
ht->node_pool = malloc(size * sizeof(Node));
|
||||
ht->pool_index = 0;
|
||||
|
||||
return ht;
|
||||
}
|
||||
|
||||
void clear_hash_table(HashTable* ht) {
|
||||
// Mette a 0 tutti i bucket e va a resettare il pool di memoria contigua
|
||||
// NOTA: non va a fare delle free perchè al prossimo utilizzo della hash table vado a sovrascrivere i nodi (verranno sempre scritti nel pool di memoria)
|
||||
memset(ht->buckets, 0, ht->size * sizeof(Node*));
|
||||
ht->pool_index = 0;
|
||||
}
|
||||
|
||||
void destroy_hash_table(HashTable** ht) {
|
||||
// Elimina completamente una hash table (anche il suo pool di memoria contigua)
|
||||
if (*ht) {
|
||||
free((*ht)->buckets);
|
||||
free((*ht)->node_pool);
|
||||
free(*ht);
|
||||
*ht = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline Node* insert_or_update_element(HashTable* ht, int x, int y, int cost) {
|
||||
// Se il nodo che sto provando ad inserire non esiste, lo inserisco in testa alla chain.
|
||||
// Se invece esiste controllo il costo: se è maggiore di quello già presente in hash table ignoro (ritorno NULL), altrimenti aggiorno il costo
|
||||
// NOTA: quando inserisco per la prima volta in hash table inizializzo heap_index a -1 per intendere che non è ancora stato inserito in heap
|
||||
int index = calculate_hash(x, y, ht->size);
|
||||
|
||||
Node* current = ht->buckets[index];
|
||||
while (current!=NULL) {
|
||||
if (current->x == x && current->y == y) {
|
||||
if (current->cost > cost) {
|
||||
current->cost = cost;
|
||||
return current;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
if (ht->pool_index >= ht->capacity) return NULL;
|
||||
|
||||
Node* new_node = &ht->node_pool[ht->pool_index++];
|
||||
new_node->x = x;
|
||||
new_node->y = y;
|
||||
new_node->cost = cost;
|
||||
new_node->heap_index = -1;
|
||||
new_node->next = ht->buckets[index];
|
||||
ht->buckets[index] = new_node;
|
||||
|
||||
return new_node;
|
||||
}
|
||||
// END
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN HEAP FUNCTIONS
|
||||
static inline void heap_swap(Node** a, Node** b) {
|
||||
// Permette di swappare due nodi (viene usato dalle heapify)
|
||||
Node* temp = *a;
|
||||
*a = *b;
|
||||
*b = temp;
|
||||
|
||||
int tmp_index = (*a)->heap_index;
|
||||
(*a)->heap_index = (*b)->heap_index;
|
||||
(*b)->heap_index = tmp_index;
|
||||
}
|
||||
|
||||
static inline void heapify_up(MinHeap* heap, int index) {
|
||||
// Fa salire un nodo dal basso verso l'alto (lo swappo con il genitore)
|
||||
// NOTA: viene usato quando inserisco un nuovo nodo: lo inserisco come foglia e poi lo faccio risalire fino alla sua posizione corretta
|
||||
int parent;
|
||||
while (index > 0) {
|
||||
parent = (index - 1) >> 1;
|
||||
if (heap->queue[index]->cost >= heap->queue[parent]->cost){
|
||||
break;
|
||||
}
|
||||
|
||||
heap_swap(&heap->queue[index], &heap->queue[parent]);
|
||||
index = parent;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void heapify_down(MinHeap* heap, int index) {
|
||||
// Fa scendere un nodo dall'alto verso il basso (lo swappo con un figlio)
|
||||
// NOTA: viene usato quando consumo il nodo minimo (root) in Dijkstra: metto il nodo più grande di tutti come root e poi lo faccio scendere fino alla posizione corretta
|
||||
int left, right, smallest;
|
||||
int size = heap->size;
|
||||
|
||||
while (true) {
|
||||
left = (index << 1) + 1;
|
||||
right = left + 1;
|
||||
smallest = index;
|
||||
|
||||
if (left < size && heap->queue[left]->cost < heap->queue[smallest]->cost) {
|
||||
smallest = left;
|
||||
}
|
||||
|
||||
if (right < size && heap->queue[right]->cost < heap->queue[smallest]->cost) {
|
||||
smallest = right;
|
||||
}
|
||||
|
||||
if (smallest == index){
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
heap_swap(&heap->queue[index], &heap->queue[smallest]);
|
||||
index = smallest;
|
||||
}
|
||||
}
|
||||
|
||||
void heap_enqueue(MinHeap* heap, Node* node) {
|
||||
// Se il nodo non è già presente in heap (index==-1) allora lo aggiungo come foglia e poi lo faccio risalire.
|
||||
// Se invece il nodo esiste già, gli aggiorno il costo e poi lo faccio risalire (il nuovo costo è per forza minore, quindi deve salire)
|
||||
// NOTA: sono sicuro che il costo nuovo sia inferiore del precedente perchè chiamo l'heap_enqueue solo dopo aver controllato tramite la hash table
|
||||
if (node->heap_index == -1) {
|
||||
if(heap->size >= heap->capacity){
|
||||
return;
|
||||
}
|
||||
|
||||
node->heap_index = heap->size;
|
||||
heap->queue[heap->size] = node;
|
||||
heap->size++;
|
||||
|
||||
heapify_up(heap, node->heap_index);
|
||||
} else {
|
||||
heapify_up(heap, node->heap_index);
|
||||
}
|
||||
}
|
||||
|
||||
Node* heap_dequeue(MinHeap* heap) {
|
||||
// Consumo il primo nodo della heap: gli imposto l'index a -1 per intendere che non è più in heap e poi lo ritorno
|
||||
// NOTA: per sistemare l'heap metto come root il nodo più grande (quello all'ultimo indice) in root e poi lo faccio scendere
|
||||
if (heap->size == 0){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Node* min = heap->queue[0];
|
||||
min->heap_index = -1;
|
||||
|
||||
heap->size--;
|
||||
|
||||
if (heap->size > 0) {
|
||||
heap->queue[0] = heap->queue[heap->size];
|
||||
heap->queue[0]->heap_index = 0;
|
||||
heapify_down(heap, 0);
|
||||
}
|
||||
|
||||
return min;
|
||||
}
|
||||
|
||||
MinHeap* heap_create(int capacity) {
|
||||
// Crea l'heap
|
||||
MinHeap* heap = malloc(sizeof(MinHeap));
|
||||
heap->queue = malloc(sizeof(Node*) * capacity);
|
||||
heap->size = 0;
|
||||
heap->capacity = capacity;
|
||||
return heap;
|
||||
}
|
||||
|
||||
void heap_clear(MinHeap* heap) {
|
||||
// Imposta semplicemente la dimensione dell'heap a 0, tanto ai successivi usi vado a sovrascrivere i puntatori che sono presenti
|
||||
heap->size = 0;
|
||||
}
|
||||
|
||||
void heap_destroy(MinHeap* heap) {
|
||||
// Vado ad eliminare completamente l'heap'
|
||||
free(heap->queue);
|
||||
free(heap);
|
||||
}
|
||||
// END
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN DIJKSTRA
|
||||
int travel_cost(HexMap* map, HashTable* ht, MinHeap* heap, int xp, int yp, int xd, int yd) {
|
||||
// Inserisco il nodo sorgente in hash table e in heap.
|
||||
// Nel while (che va avanti finchè non esaurisco i nodi nell'heap) faccio:
|
||||
// - Prendo il primo elemento dall'heap (quindi il minimo)
|
||||
// - Se è il nodo destinazione, ritorno il costo di raggiungimento (non mi serve esplorare ulteriormente per come è fatto Dijkstra, appena lo trovo ho già trovato il costo minore)
|
||||
// - Controllo i 6 nodi vicini e, se il loro costo di raggiungimento è minore di quello che già hanno in hash table (oppure se vengono inseriti per la prima volta in hash table), vado ad inserirli anche in heap
|
||||
// - Controllo tutte le rotte aeree del nodo e, se il nodo di destinazione ha costo di raggiungimento minore di quello già presente in hash table, lo inserisco in heap
|
||||
// Controllo alla fine il costo di raggiungimento del nodo di destinazione (teoricamente non dovrei mai arrivarci qui)
|
||||
// Pulisco heap e hash table
|
||||
if (!is_valid(map,xp,yp) || !is_valid(map,xd,yd)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Node* new_node = insert_or_update_element(ht, xp, yp, 0);
|
||||
heap_enqueue(heap, new_node);
|
||||
|
||||
|
||||
Node* current;
|
||||
Hexagon* hex;
|
||||
|
||||
static const int dir_even[6][2] = {
|
||||
{+1, 0}, { 0, -1}, {+1, -1},
|
||||
{-1, 0}, {+1, +1}, { 0, +1}
|
||||
};
|
||||
static const int dir_odd[6][2] = {
|
||||
{+1, 0}, {-1, -1}, { 0, -1},
|
||||
{-1, 0}, { 0, +1}, {-1, +1}
|
||||
};
|
||||
const int (*dirs)[2];
|
||||
|
||||
int new_x, new_y;
|
||||
|
||||
|
||||
while (heap->size > 0) {
|
||||
current = heap_dequeue(heap);
|
||||
|
||||
if (current->x == xd && current->y == yd) {
|
||||
int result = current->cost;
|
||||
heap_clear(heap);
|
||||
clear_hash_table(ht);
|
||||
return result;
|
||||
}
|
||||
|
||||
hex = &map->grid[current->x][current->y];
|
||||
if (hex->land_cost == 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current->y % 2 == 1) {
|
||||
dirs = dir_even;
|
||||
} else {
|
||||
dirs = dir_odd;
|
||||
}
|
||||
|
||||
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)) {
|
||||
new_node = insert_or_update_element(ht, new_x, new_y, current->cost + hex->land_cost);
|
||||
if (new_node != NULL) {
|
||||
heap_enqueue(heap, new_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < hex->air_route_count; i++) {
|
||||
new_x = hex->air_routes[i].dest_x;
|
||||
new_y = hex->air_routes[i].dest_y;
|
||||
|
||||
if (is_valid(map, new_x, new_y)) {
|
||||
new_node = insert_or_update_element(ht, new_x, new_y, current->cost + hex->air_routes[i].cost);
|
||||
if (new_node != NULL) {
|
||||
heap_enqueue(heap, new_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int index = calculate_hash(xd, yd, ht->size);
|
||||
current = ht->buckets[index];
|
||||
while (current != NULL) {
|
||||
if (current->x == xd && current->y == yd) {
|
||||
int result = current->cost;
|
||||
heap_clear(heap);
|
||||
clear_hash_table(ht);
|
||||
return result;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
heap_clear(heap);
|
||||
clear_hash_table(ht);
|
||||
return -1;
|
||||
}
|
||||
// END
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN CACHE
|
||||
// DA SISTEMARE
|
||||
CACHE_HashTable* create_cache(int size){
|
||||
static const int primes[] = {
|
||||
7, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593,
|
||||
49157, 98317, 196613, 393241, 786433, 1572869, 3145739,
|
||||
6291469, 12582917, 25165843, 50331653, 100663319, 201326611
|
||||
};
|
||||
|
||||
int hash_length=0;
|
||||
for (size_t i = 0; i < 23; i++) {
|
||||
if (primes[i] >= size) {
|
||||
hash_length = primes[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CACHE_HashTable* ht = (CACHE_HashTable*)malloc(sizeof(CACHE_HashTable));
|
||||
ht->size = hash_length;
|
||||
ht->occupied=0;
|
||||
|
||||
ht->buckets = (CACHE_HashNode**) malloc(hash_length * sizeof(CACHE_HashNode*));
|
||||
for (int i = 0; i < hash_length; i++) {
|
||||
ht->buckets[i] = NULL;
|
||||
}
|
||||
|
||||
return ht;
|
||||
}
|
||||
|
||||
void clear_cache(CACHE_HashTable* ht) {
|
||||
for (int i = 0; i < ht->size; i++) {
|
||||
CACHE_HashNode* current = ht->buckets[i];
|
||||
|
||||
while (current != NULL) {
|
||||
CACHE_HashNode* next = current->next;
|
||||
free(current);
|
||||
current = next;
|
||||
}
|
||||
|
||||
ht->buckets[i] = NULL;
|
||||
}
|
||||
|
||||
ht->occupied=0;
|
||||
}
|
||||
|
||||
void destroy_cache(CACHE_HashTable** ht) {
|
||||
clear_cache(*ht);
|
||||
|
||||
free((*ht)->buckets);
|
||||
free(*ht);
|
||||
*ht = NULL;
|
||||
}
|
||||
|
||||
int calculate_hash_cache(int xp, int yp, int xd, int yd, int size) {
|
||||
const int p1 = 7919;
|
||||
const int p2 = 1237;
|
||||
const int p3 = 1047;
|
||||
const int p4 = 1548;
|
||||
|
||||
return ((xp * p1) ^ (yp * p2) ^ (xd * p3) ^ (yd * p4)) % size;
|
||||
}
|
||||
|
||||
void delete_element_cache(CACHE_HashTable* ht){
|
||||
CACHE_HashNode* prev=NULL;
|
||||
|
||||
CACHE_HashNode* min=NULL;
|
||||
CACHE_HashNode* min_prev=NULL;
|
||||
int min_LRU=1000;
|
||||
int min_index=0;
|
||||
|
||||
for (int i = 0; i < ht->size; i++) {
|
||||
CACHE_HashNode* current = ht->buckets[i];
|
||||
prev = NULL;
|
||||
|
||||
while (current != NULL) {
|
||||
if (current->LRU < min_LRU){
|
||||
min_LRU=current->LRU;
|
||||
min=current;
|
||||
min_prev=prev;
|
||||
min_index=i;
|
||||
}
|
||||
prev = current;
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (min_prev==NULL){
|
||||
ht->buckets[min_index] = min->next;
|
||||
} else {
|
||||
min_prev->next=min->next;
|
||||
}
|
||||
|
||||
free(min);
|
||||
}
|
||||
|
||||
bool insert_element_cache (CACHE_HashTable* ht, int xp, int yp, int xd, int yd, int travel_cost){
|
||||
int index = calculate_hash_cache(xp, yp, xd, yd, ht->size);
|
||||
|
||||
CACHE_HashNode* current = ht->buckets[index];
|
||||
while (current!=NULL && !(current->xp==xp && current->yp==yp && current->xd==xd && current->yd==yd)){
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
if (current==NULL){
|
||||
if (ht->occupied>=CACHE_MAX_OCCUPIED){
|
||||
delete_element_cache(ht);
|
||||
}
|
||||
|
||||
CACHE_HashNode* hn = malloc(sizeof(CACHE_HashNode));
|
||||
hn->xp = xp;
|
||||
hn->yp = yp;
|
||||
hn->xd = xd;
|
||||
hn->yd = yd;
|
||||
hn->travel_cost=travel_cost;
|
||||
ht->occupied=ht->occupied+1;
|
||||
|
||||
hn->next = ht->buckets[index];
|
||||
ht->buckets[index] = hn;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int search_element_cache (CACHE_HashTable* ht, int xp, int yp, int xd, int yd){
|
||||
int index = calculate_hash_cache(xp, yp, xd, yd, ht->size);
|
||||
|
||||
CACHE_HashNode* current = ht->buckets[index];
|
||||
while (current != NULL && !(current->xp==xp && current->yp==yp && current->xd==xd && current->yd==yd)){
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
if(current==NULL){
|
||||
return -2;
|
||||
}
|
||||
|
||||
current->LRU=current->LRU+1;
|
||||
return current->travel_cost;
|
||||
}
|
||||
// END
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN MAIN
|
||||
int main(){
|
||||
char testo[30];
|
||||
int inp_uno, inp_due, inp_tre, inp_quattro;
|
||||
|
||||
HexMap map;
|
||||
HashTable* ht = NULL;
|
||||
MinHeap* heap = NULL;
|
||||
CACHE_HashTable* cache = NULL;
|
||||
|
||||
int already_initialized=0;
|
||||
int cost;
|
||||
|
||||
while (true){
|
||||
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){
|
||||
|
||||
// MAPPA
|
||||
if (already_initialized==1){
|
||||
destroy_map(&map);
|
||||
}
|
||||
init_map(&map, inp_uno, inp_due);
|
||||
already_initialized=1;
|
||||
|
||||
// HASH TABLE
|
||||
if (ht!=NULL){
|
||||
destroy_hash_table(&ht);
|
||||
}
|
||||
ht = create_hash_table(inp_uno * inp_due);
|
||||
|
||||
// HEAP
|
||||
if (heap!=NULL){
|
||||
heap_destroy(heap);
|
||||
}
|
||||
heap = heap_create(inp_uno * inp_due);
|
||||
|
||||
// CACHE
|
||||
if (cache!=NULL){
|
||||
destroy_cache(&cache);
|
||||
}
|
||||
cache = create_cache(CACHE_SIZE);
|
||||
}
|
||||
|
||||
|
||||
if (strcmp(testo, "print")==0){
|
||||
if (already_initialized==0){
|
||||
fprintf(stdout, "-1\n");
|
||||
fflush(stdout);
|
||||
continue;
|
||||
}
|
||||
print_map(&map);
|
||||
}
|
||||
|
||||
|
||||
if (strcmp(testo, "change_cost")==0){
|
||||
if (already_initialized==0){
|
||||
fprintf(stdout, "-1\n");
|
||||
fflush(stdout);
|
||||
continue;
|
||||
}
|
||||
if(change_cost(&map, inp_uno, inp_due, inp_tre, inp_quattro)){
|
||||
clear_cache(cache);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (strcmp(testo, "toggle_air_route")==0){
|
||||
if (already_initialized==0){
|
||||
fprintf(stdout, "-1\n");
|
||||
fflush(stdout);
|
||||
continue;
|
||||
}
|
||||
if(toggle_air_route(&map, inp_uno, inp_due, inp_tre, inp_quattro)){
|
||||
clear_cache(cache);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (strcmp(testo, "travel_cost")==0){
|
||||
if (already_initialized==0){
|
||||
fprintf(stdout, "-1\n");
|
||||
fflush(stdout);
|
||||
continue;
|
||||
}
|
||||
cost=search_element_cache(cache, inp_uno, inp_due, inp_tre, inp_quattro);
|
||||
|
||||
if(cost==-2){
|
||||
cost = travel_cost(&map, ht, heap, inp_uno, inp_due, inp_tre, inp_quattro);
|
||||
insert_element_cache(cache, inp_uno, inp_due, inp_tre, inp_quattro, cost);
|
||||
}
|
||||
|
||||
fprintf(stdout, "%d\n", cost);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
if (already_initialized==1) {
|
||||
destroy_map(&map);
|
||||
}
|
||||
if (ht!=NULL) {
|
||||
destroy_hash_table(&ht);
|
||||
}
|
||||
if (heap!=NULL) {
|
||||
heap_destroy(heap);
|
||||
}
|
||||
if (cache!=NULL) {
|
||||
destroy_cache(&cache);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
// END
|
||||
@@ -0,0 +1,883 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <math.h>
|
||||
|
||||
#define CACHE_SIZE 2048
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN STRUCTURES
|
||||
typedef struct Node { // Nodo puntato sia dalla hash table che dall'heap. Usato in Dijkstra
|
||||
int x, y; // Coordinate
|
||||
int cost; // Costo di raggiungimento
|
||||
int heap_index; // Indice nell'heap
|
||||
struct Node* next; // Puntatore al prossimo elemento (hash table con chaining)
|
||||
} Node;
|
||||
|
||||
typedef struct { // Hash table
|
||||
int size; // Quantità di nodi presenti
|
||||
int capacity; // Capacità massima
|
||||
Node** buckets; // Puntatore all'array di puntatori ai Node
|
||||
Node* pool; // Puntatore alla pool di memoria contigua
|
||||
int pool_index; // Indice del pool di memoria contigua
|
||||
} HashTable;
|
||||
|
||||
typedef struct MinHeap { // Heap
|
||||
int size; // Quantità di nodi presenti
|
||||
int capacity; // Capacità massima
|
||||
Node** queue; // Puntatore all'array di puntatori ai Node
|
||||
} MinHeap;
|
||||
|
||||
typedef struct { // Rotta aerea
|
||||
int dest_x, dest_y; // Destinazione
|
||||
int cost; // Costo di raggiungimento
|
||||
} AirRoute;
|
||||
|
||||
typedef struct { // Esagono
|
||||
int land_cost; // Costo uscita esagono
|
||||
AirRoute air_routes[5]; // Array di rotte aeree
|
||||
int air_route_count; // Quantità di rotte aeree presenti
|
||||
} Hexagon;
|
||||
|
||||
typedef struct { // Mappa di esagoni
|
||||
int rows, cols; // Numero di righe e colonne
|
||||
Hexagon** grid; // Matrice vera e propria
|
||||
} HexMap;
|
||||
|
||||
typedef struct CacheNode { // Nodo della cache
|
||||
int xp, yp; // Coordinate di partenza
|
||||
int xd, yd; // Coordinate di arrivo
|
||||
int cost; // Costo di percorrenza
|
||||
struct CacheNode* next; // Puntatore al prossimo elemento (hash table chained)
|
||||
struct CacheNode* lru_prev; // Puntatore all'elemento precedente nella double linked list LRU
|
||||
struct CacheNode* lru_next; // Puntatore al prossimo elemento nella double linked list LRU
|
||||
} CacheNode;
|
||||
|
||||
typedef struct { // Hash table della cache
|
||||
int size; // Quantità di bucket
|
||||
int capacity; // Quantità massima di nodi prima di iniziare ad attuare la policy di LRU
|
||||
int element_number; // Quantità attuale di elementi nella cache
|
||||
CacheNode** buckets; // Puntatore all'array di puntatori ai CacheNode
|
||||
CacheNode* pool; // Puntatore alla pool di memoria contigua
|
||||
int pool_index; // Indice del pool di memoria contigua
|
||||
CacheNode* head; // CacheNode più recentemente usato
|
||||
CacheNode* tail; // CacheNode meno recentemente usato
|
||||
} CacheHashTable;
|
||||
// END
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN MAP FUNCTIONS
|
||||
void init_map(HexMap* map, int cols, int rows){
|
||||
// Inizializza la griglia: prende in input il puntatore alla struttura mappa, righe e colonne e crea la griglia formata da un array di puntatori ad array
|
||||
map->rows = rows;
|
||||
map->cols = cols;
|
||||
|
||||
map->grid = (Hexagon**) malloc(cols * sizeof(Hexagon*));
|
||||
|
||||
for (int i=0; i<map->cols; i++){
|
||||
map->grid[i] = (Hexagon*) malloc(rows * sizeof(Hexagon));
|
||||
|
||||
for (int j=0; j<map->rows; j++){
|
||||
map->grid[i][j].land_cost=1;
|
||||
map->grid[i][j].air_route_count = 0;
|
||||
}
|
||||
}
|
||||
fprintf(stdout,"OK\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void destroy_map(HexMap* map){
|
||||
// Distrugge 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){
|
||||
// Stampa la mappa
|
||||
printf("\n");
|
||||
for (int i=map->rows-1; i>=0; i--){
|
||||
if(i%2==1) printf(" ");
|
||||
for (int j=0; j<map->cols; j++){
|
||||
printf("%d ",map->grid[j][i].land_cost);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
inline bool is_valid(HexMap* map, int x, int y){
|
||||
// Controlla se delle coordinate sono entro i bordi della mappa
|
||||
return !(x<0 || x>map->cols-1 || y<0 || y>map->rows-1);
|
||||
}
|
||||
|
||||
int hexagons_distance(int x_1, int y_1, int x_2, int y_2){
|
||||
// Calcola la distanza tra due esagoni tramite le coordinate cubiche (non ho idea di come funzioni, fidati)
|
||||
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)));
|
||||
}
|
||||
|
||||
bool change_cost(HexMap* map, int x, int y, int cost, int radius){
|
||||
// Cambia il costo nella mappa: per ogni esagono nella mappa calcola la distanza dal nodo sorgente, se è inferiore del raggio allora cambia il costo dell'esagono secondo la formula. Successivamente aggiorna i costi delle rotte aeree
|
||||
// NOTA: il costo può variare massimo tra 0 e 100
|
||||
if(!is_valid(map, x, y) || radius<=0 || cost < -10 || cost > 10){
|
||||
fputs("KO\n", stdout);
|
||||
fflush(stdout);
|
||||
return false;
|
||||
}
|
||||
|
||||
int dist;
|
||||
int cost_old_air_route;
|
||||
float coeff;
|
||||
for(int i=0; i<map->cols; i++){
|
||||
for (int j=0; j<map->rows; j++){
|
||||
dist=hexagons_distance(x,y,i,j);
|
||||
if (dist<radius){
|
||||
if(((float)(radius - dist) / (float)radius)>0){
|
||||
coeff=((float)(radius - dist) / (float)radius);
|
||||
} else {
|
||||
coeff=0;
|
||||
}
|
||||
|
||||
map->grid[i][j].land_cost=map->grid[i][j].land_cost+(int)floor(cost * coeff);
|
||||
|
||||
if (map->grid[i][j].land_cost>100){
|
||||
map->grid[i][j].land_cost=100;
|
||||
}
|
||||
if (map->grid[i][j].land_cost<0){
|
||||
map->grid[i][j].land_cost=0;
|
||||
}
|
||||
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool toggle_air_route(HexMap* map, int x_1, int y_1, int x_2, int y_2){
|
||||
// Inserisce o rimuove una rotta aerea da un esagono
|
||||
if(!is_valid(map, x_1,y_1) || !is_valid(map, x_2,y_2)){
|
||||
fputs("KO\n", stdout);
|
||||
fflush(stdout);
|
||||
return false;
|
||||
}
|
||||
|
||||
Hexagon* StartingHexagon = &(map->grid[x_1][y_1]);
|
||||
|
||||
if (StartingHexagon->air_route_count>4){
|
||||
fputs("KO\n", stdout);
|
||||
fflush(stdout);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i=0; i< StartingHexagon->air_route_count; i++){
|
||||
if ((StartingHexagon->air_routes[i].dest_x == x_2)&&(StartingHexagon->air_routes[i].dest_y == y_2)){
|
||||
for (int j=i; j<StartingHexagon->air_route_count; j++){
|
||||
StartingHexagon->air_routes[j]=StartingHexagon->air_routes[j+1];
|
||||
}
|
||||
StartingHexagon->air_route_count = StartingHexagon->air_route_count - 1;
|
||||
fputs("OK\n", stdout);
|
||||
fflush(stdout);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
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};
|
||||
StartingHexagon->air_route_count = StartingHexagon->air_route_count + 1;
|
||||
fputs("OK\n", stdout);
|
||||
fflush(stdout);
|
||||
return true;
|
||||
}
|
||||
// END
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN HASH-TABLE FUNCTIONS
|
||||
inline int calculate_hash(int x, int y, int size){
|
||||
// Calcola l'hash dati in input le coordinate e la dimensione della hash table
|
||||
return ((x * 73 + y * 31) % size);
|
||||
}
|
||||
|
||||
HashTable* create_hash_table(int size){
|
||||
// Genera l'hash table con fattore di carico 1.5 per bilanciare performance temporali e spaziali. Calcola il numero primo migliore da usare come dimensione dell'hash table e poi inizializza tutti i suoi attributi
|
||||
// NOTA: con calloc() vado a inizializzare già tutti i bucket a 0, quindi non devo preoccuparmi di farli puntare a NULL
|
||||
int min_size = (int)ceil(size / 1.5);
|
||||
|
||||
int hash_length;
|
||||
if (min_size <= 53) {
|
||||
hash_length = 53;
|
||||
} else if (min_size <= 97) {
|
||||
hash_length = 97;
|
||||
} else if (min_size <= 193) {
|
||||
hash_length = 193;
|
||||
} else if (min_size <= 389) {
|
||||
hash_length = 389;
|
||||
} else if (min_size <= 769) {
|
||||
hash_length = 769;
|
||||
} else if (min_size <= 1543) {
|
||||
hash_length = 1543;
|
||||
} else if (min_size <= 3079) {
|
||||
hash_length = 3079;
|
||||
} else if (min_size <= 6151) {
|
||||
hash_length = 6151;
|
||||
} else if (min_size <= 12289) {
|
||||
hash_length = 12289;
|
||||
} else if (min_size <= 24593) {
|
||||
hash_length = 24593;
|
||||
} else if (min_size <= 49157) {
|
||||
hash_length = 49157;
|
||||
} else if (min_size <= 98317) {
|
||||
hash_length = 98317;
|
||||
} else if (min_size <= 196613) {
|
||||
hash_length = 196613;
|
||||
} else if (min_size <= 393241) {
|
||||
hash_length = 393241;
|
||||
} else if (min_size <= 786433) {
|
||||
hash_length = 786433;
|
||||
} else {
|
||||
hash_length = 1572869;
|
||||
}
|
||||
|
||||
HashTable* ht = (HashTable*) malloc(sizeof(HashTable));
|
||||
ht->size = hash_length;
|
||||
ht->capacity = size;
|
||||
ht->buckets = calloc(hash_length, sizeof(Node*));
|
||||
ht->pool = malloc(size * sizeof(Node));
|
||||
ht->pool_index = 0;
|
||||
|
||||
return ht;
|
||||
}
|
||||
|
||||
void clear_hash_table(HashTable* ht){
|
||||
// Mette a 0 tutti i bucket e va a resettare il pool di memoria contigua
|
||||
// NOTA: non va a fare delle free perchè al prossimo utilizzo della hash table vado a sovrascrivere i nodi (verranno sempre scritti nel pool di memoria)
|
||||
memset(ht->buckets, 0, ht->size * sizeof(Node*));
|
||||
ht->pool_index = 0;
|
||||
}
|
||||
|
||||
void destroy_hash_table(HashTable** ht){
|
||||
// Elimina completamente una hash table (anche il suo pool di memoria contigua)
|
||||
if (*ht) {
|
||||
free((*ht)->buckets);
|
||||
free((*ht)->pool);
|
||||
free(*ht);
|
||||
*ht = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
inline Node* insert_or_update_element(HashTable* ht, int x, int y, int cost){
|
||||
// Se il nodo che sto provando ad inserire non esiste, lo inserisco in testa alla chain.
|
||||
// Se invece esiste controllo il costo: se è maggiore di quello già presente in hash table ignoro (ritorno NULL), altrimenti aggiorno il costo
|
||||
// NOTA: quando inserisco per la prima volta in hash table inizializzo heap_index a -1 per intendere che non è ancora stato inserito in heap
|
||||
int index = calculate_hash(x, y, ht->size);
|
||||
|
||||
Node* current = ht->buckets[index];
|
||||
while (current!=NULL) {
|
||||
if (current->x == x && current->y == y) {
|
||||
if (current->cost > cost) {
|
||||
current->cost = cost;
|
||||
return current;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
if (ht->pool_index >= ht->capacity){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Node* new_node = &ht->pool[ht->pool_index++];
|
||||
new_node->x = x;
|
||||
new_node->y = y;
|
||||
new_node->cost = cost;
|
||||
new_node->heap_index = -1;
|
||||
new_node->next = ht->buckets[index];
|
||||
ht->buckets[index] = new_node;
|
||||
|
||||
return new_node;
|
||||
}
|
||||
// END
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN HEAP FUNCTIONS
|
||||
inline void heap_swap(Node** a, Node** b){
|
||||
// Permette di swappare due nodi (viene usato dalle heapify)
|
||||
Node* temp = *a;
|
||||
*a = *b;
|
||||
*b = temp;
|
||||
|
||||
int tmp_index = (*a)->heap_index;
|
||||
(*a)->heap_index = (*b)->heap_index;
|
||||
(*b)->heap_index = tmp_index;
|
||||
}
|
||||
|
||||
inline void heapify_up(MinHeap* heap, int index){
|
||||
// Fa salire un nodo dal basso verso l'alto (lo swappo con il genitore)
|
||||
// NOTA: viene usato quando inserisco un nuovo nodo: lo inserisco come foglia e poi lo faccio risalire fino alla sua posizione corretta
|
||||
int parent;
|
||||
while (index > 0) {
|
||||
parent = (index - 1) / 2;
|
||||
if (heap->queue[index]->cost >= heap->queue[parent]->cost){
|
||||
break;
|
||||
}
|
||||
|
||||
heap_swap(&heap->queue[index], &heap->queue[parent]);
|
||||
index = parent;
|
||||
}
|
||||
}
|
||||
|
||||
inline void heapify_down(MinHeap* heap, int index){
|
||||
// Fa scendere un nodo dall'alto verso il basso (lo swappo con un figlio)
|
||||
// NOTA: viene usato quando consumo il nodo minimo (root) in Dijkstra: metto il nodo più grande di tutti come root e poi lo faccio scendere fino alla posizione corretta
|
||||
int left, right, smallest;
|
||||
int size = heap->size;
|
||||
|
||||
while (true) {
|
||||
left = (index * 2) + 1;
|
||||
right = left + 1;
|
||||
smallest = index;
|
||||
|
||||
if (left < size && heap->queue[left]->cost < heap->queue[smallest]->cost) {
|
||||
smallest = left;
|
||||
}
|
||||
|
||||
if (right < size && heap->queue[right]->cost < heap->queue[smallest]->cost) {
|
||||
smallest = right;
|
||||
}
|
||||
|
||||
if (smallest == index){
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
heap_swap(&heap->queue[index], &heap->queue[smallest]);
|
||||
index = smallest;
|
||||
}
|
||||
}
|
||||
|
||||
void heap_enqueue(MinHeap* heap, Node* node){
|
||||
// Se il nodo non è già presente in heap (index==-1) allora lo aggiungo come foglia e poi lo faccio risalire.
|
||||
// Se invece il nodo esiste già, gli aggiorno il costo e poi lo faccio risalire (il nuovo costo è per forza minore, quindi deve salire)
|
||||
// NOTA: sono sicuro che il costo nuovo sia inferiore del precedente perchè chiamo l'heap_enqueue solo dopo aver controllato tramite la hash table
|
||||
if (node->heap_index == -1) {
|
||||
if(heap->size >= heap->capacity){
|
||||
return;
|
||||
}
|
||||
|
||||
node->heap_index = heap->size;
|
||||
heap->queue[heap->size] = node;
|
||||
heap->size++;
|
||||
|
||||
heapify_up(heap, node->heap_index);
|
||||
} else {
|
||||
heapify_up(heap, node->heap_index);
|
||||
}
|
||||
}
|
||||
|
||||
Node* heap_dequeue(MinHeap* heap){
|
||||
// Consumo il primo nodo della heap: gli imposto l'index a -1 per intendere che non è più in heap e poi lo ritorno
|
||||
// NOTA: per sistemare l'heap metto come root il nodo più grande (quello all'ultimo indice) in root e poi lo faccio scendere
|
||||
if (heap->size == 0){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Node* min = heap->queue[0];
|
||||
min->heap_index = -1;
|
||||
|
||||
heap->size--;
|
||||
|
||||
if (heap->size > 0) {
|
||||
heap->queue[0] = heap->queue[heap->size];
|
||||
heap->queue[0]->heap_index = 0;
|
||||
heapify_down(heap, 0);
|
||||
}
|
||||
|
||||
return min;
|
||||
}
|
||||
|
||||
MinHeap* heap_create(int capacity){
|
||||
// Crea l'heap
|
||||
MinHeap* heap = malloc(sizeof(MinHeap));
|
||||
heap->queue = malloc(sizeof(Node*) * capacity);
|
||||
heap->size = 0;
|
||||
heap->capacity = capacity;
|
||||
return heap;
|
||||
}
|
||||
|
||||
void heap_clear(MinHeap* heap){
|
||||
// Imposta semplicemente la dimensione dell'heap a 0, tanto ai successivi usi vado a sovrascrivere i puntatori che sono presenti
|
||||
heap->size = 0;
|
||||
}
|
||||
|
||||
void heap_destroy(MinHeap* heap){
|
||||
// Vado ad eliminare completamente l'heap'
|
||||
free(heap->queue);
|
||||
free(heap);
|
||||
}
|
||||
// END
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN DIJKSTRA
|
||||
int travel_cost(HexMap* map, HashTable* ht, MinHeap* heap, int xp, int yp, int xd, int yd){
|
||||
// Inserisco il nodo sorgente in hash table e in heap.
|
||||
// Nel while (che va avanti finchè non esaurisco i nodi nell'heap) faccio:
|
||||
// - Prendo il primo elemento dall'heap (quindi il minimo)
|
||||
// - Se è il nodo destinazione, ritorno il costo di raggiungimento (non mi serve esplorare ulteriormente per come è fatto Dijkstra, appena lo trovo ho già trovato il costo minore)
|
||||
// - Controllo i 6 nodi vicini e, se il loro costo di raggiungimento è minore di quello che già hanno in hash table (oppure se vengono inseriti per la prima volta in hash table), vado ad inserirli anche in heap
|
||||
// - Controllo tutte le rotte aeree del nodo e, se il nodo di destinazione ha costo di raggiungimento minore di quello già presente in hash table, lo inserisco in heap
|
||||
// Controllo alla fine il costo di raggiungimento del nodo di destinazione (teoricamente non dovrei mai arrivarci qui)
|
||||
// Pulisco heap e hash table
|
||||
if (!is_valid(map,xp,yp) || !is_valid(map,xd,yd)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Node* new_node = insert_or_update_element(ht, xp, yp, 0);
|
||||
heap_enqueue(heap, new_node);
|
||||
|
||||
|
||||
Node* current;
|
||||
Hexagon* hex;
|
||||
|
||||
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}
|
||||
};
|
||||
const int (*dirs)[2];
|
||||
|
||||
int new_x, new_y;
|
||||
|
||||
|
||||
while (heap->size > 0) {
|
||||
current = heap_dequeue(heap);
|
||||
|
||||
if (current->x == xd && current->y == yd) {
|
||||
int result = current->cost;
|
||||
heap_clear(heap);
|
||||
clear_hash_table(ht);
|
||||
return result;
|
||||
}
|
||||
|
||||
hex = &map->grid[current->x][current->y];
|
||||
if (hex->land_cost == 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current->y % 2 == 1) {
|
||||
dirs = dir_even;
|
||||
} else {
|
||||
dirs = dir_odd;
|
||||
}
|
||||
|
||||
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)) {
|
||||
new_node = insert_or_update_element(ht, new_x, new_y, current->cost + hex->land_cost);
|
||||
if (new_node != NULL) {
|
||||
heap_enqueue(heap, new_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < hex->air_route_count; i++) {
|
||||
new_x = hex->air_routes[i].dest_x;
|
||||
new_y = hex->air_routes[i].dest_y;
|
||||
|
||||
if (is_valid(map, new_x, new_y)) {
|
||||
new_node = insert_or_update_element(ht, new_x, new_y, current->cost + hex->air_routes[i].cost);
|
||||
if (new_node != NULL) {
|
||||
heap_enqueue(heap, new_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int index = calculate_hash(xd, yd, ht->size);
|
||||
current = ht->buckets[index];
|
||||
while (current != NULL) {
|
||||
if (current->x == xd && current->y == yd) {
|
||||
int result = current->cost;
|
||||
heap_clear(heap);
|
||||
clear_hash_table(ht);
|
||||
return result;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
heap_clear(heap);
|
||||
clear_hash_table(ht);
|
||||
return -1;
|
||||
}
|
||||
// END
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN CACHE FUNCTIONS
|
||||
inline int calculate_cache_hash(int xp, int yp, int xd, int yd, int size){
|
||||
// Calcola l'hash dati in input le quattro coordinate e la dimensione della hash table della cache
|
||||
return ((xp * 1009 + yp * 1013 + xd * 1019 + yd * 1021) % size);
|
||||
}
|
||||
|
||||
CacheHashTable* create_cache(int capacity){
|
||||
// Crea la cache con un fattore di carico di circa 1.2 (per massimizzare efficienza temporale e spaziale).
|
||||
// Inizializza tutti gli attributi della tabella hash stessa (dimensione, capacità, pool ecc...) e e anche della lista doppiamente concatenata LRU (head e tail)
|
||||
int min_size = (int)ceil(capacity / 1.2);
|
||||
|
||||
int hash_length;
|
||||
if (min_size <= 53){
|
||||
hash_length = 53;
|
||||
} else if (min_size <= 97){
|
||||
hash_length = 97;
|
||||
} else if (min_size <= 193){
|
||||
hash_length = 193;
|
||||
} else if (min_size <= 389){
|
||||
hash_length = 389;
|
||||
} else if (min_size <= 769){
|
||||
hash_length = 769;
|
||||
} else if (min_size <= 1543){
|
||||
hash_length = 1543;
|
||||
} else if (min_size <= 3079){
|
||||
hash_length = 3079;
|
||||
} else{
|
||||
hash_length = 6151;
|
||||
}
|
||||
|
||||
|
||||
// Hash table
|
||||
CacheHashTable* cache = (CacheHashTable*)malloc(sizeof(CacheHashTable));
|
||||
cache->size = hash_length;
|
||||
cache->capacity = capacity;
|
||||
cache->element_number = 0;
|
||||
cache->buckets = calloc(hash_length, sizeof(CacheNode*));
|
||||
cache->pool = malloc(capacity * sizeof(CacheNode));
|
||||
cache->pool_index = 0;
|
||||
|
||||
// Lista LRU
|
||||
cache->head = &cache->pool[cache->pool_index++];
|
||||
cache->tail = &cache->pool[cache->pool_index++];
|
||||
cache->head->lru_next = cache->tail;
|
||||
cache->tail->lru_prev = cache->head;
|
||||
cache->head->lru_prev = NULL;
|
||||
cache->tail->lru_next = NULL;
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
inline void lru_move_to_head(CacheHashTable* cache, CacheNode* node){
|
||||
// Prende in ingresso un nodo già presente in lista e lo inserisce in testa (quando faccio una lookup deve andare in testa)
|
||||
|
||||
// Gestisce i puntatori prima di spostare il nodo
|
||||
if (node->lru_prev){
|
||||
node->lru_prev->lru_next = node->lru_next;
|
||||
}
|
||||
if (node->lru_next){
|
||||
node->lru_next->lru_prev = node->lru_prev;
|
||||
}
|
||||
|
||||
// Sposta il nodo in testa
|
||||
node->lru_next = cache->head->lru_next;
|
||||
node->lru_prev = cache->head;
|
||||
cache->head->lru_next->lru_prev = node;
|
||||
cache->head->lru_next = node;
|
||||
}
|
||||
|
||||
inline void remove_node(CacheHashTable* cache, CacheNode* node, int xp, int yp, int xd, int yd){
|
||||
// Rimuove un nodo sia dalla lista che dalla hash table
|
||||
|
||||
// Rimozione dalla hash table
|
||||
int index = calculate_cache_hash(xp, yp, xd, yd, cache->size);
|
||||
CacheNode* current = cache->buckets[index];
|
||||
CacheNode* prev = NULL;
|
||||
|
||||
while (current != NULL) {
|
||||
if (current == node) {
|
||||
if (prev == NULL) {
|
||||
cache->buckets[index] = current->next;
|
||||
} else {
|
||||
prev->next = current->next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
prev = current;
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
// Rimozione dalla lista LRU
|
||||
if (node->lru_prev) node->lru_prev->lru_next = node->lru_next;
|
||||
if (node->lru_next) node->lru_next->lru_prev = node->lru_prev;
|
||||
|
||||
cache->element_number--;
|
||||
}
|
||||
|
||||
CacheNode* cache_lookup(CacheHashTable* cache, int xp, int yp, int xd, int yd){
|
||||
// Cerca un nodo in cache e, se lo trova, lo muove in testa nella lista LRU
|
||||
int index = calculate_cache_hash(xp, yp, xd, yd, cache->size);
|
||||
CacheNode* current = cache->buckets[index];
|
||||
|
||||
while (current != NULL) {
|
||||
if (current->xp == xp && current->yp == yp && current->xd == xd && current->yd == yd) { // Se viene trovato il nodo
|
||||
lru_move_to_head(cache, current);
|
||||
return current;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
return NULL; // Se non viene trovato, restituisco NULL
|
||||
}
|
||||
|
||||
CacheNode* cache_insert(CacheHashTable* cache, int xp, int yp, int xd, int yd, int cost){
|
||||
// Cerca nell'hash table se il nodo esiste già (nella lookup c'è già lo spostamento in testa). Nel caso aggiorna il costo
|
||||
CacheNode* existing = cache_lookup(cache, xp, yp, xd, yd);
|
||||
if (existing != NULL) {
|
||||
existing->cost = cost;
|
||||
return existing;
|
||||
}
|
||||
|
||||
// Se la cache è piena, rimuovo il nodo in coda
|
||||
if (cache->element_number >= cache->capacity) {
|
||||
CacheNode* lru_node = cache->tail->lru_prev;
|
||||
if (lru_node != cache->head) { // Se la lista LRU non è vuota
|
||||
remove_node(cache, lru_node, lru_node->xp, lru_node->yp, lru_node->xd, lru_node->yd);
|
||||
}
|
||||
}
|
||||
|
||||
// Arrivo qua solamente se il nodo non è già presente in cache; creo il nodo
|
||||
CacheNode* new_node = &cache->pool[cache->pool_index++];
|
||||
new_node->xp = xp;
|
||||
new_node->yp = yp;
|
||||
new_node->xd = xd;
|
||||
new_node->yd = yd;
|
||||
new_node->cost = cost;
|
||||
|
||||
// Lo aggiungo in hash table
|
||||
int index = calculate_cache_hash(xp, yp, xd, yd, cache->size);
|
||||
new_node->next = cache->buckets[index];
|
||||
cache->buckets[index] = new_node;
|
||||
|
||||
// Lo aggiungo in testa alla coda
|
||||
new_node->lru_next = cache->head->lru_next;
|
||||
new_node->lru_prev = cache->head;
|
||||
cache->head->lru_next->lru_prev = new_node;
|
||||
cache->head->lru_next = new_node;
|
||||
|
||||
cache->element_number++;
|
||||
return new_node;
|
||||
}
|
||||
|
||||
void cache_remove(CacheHashTable* cache, int xp, int yp, int xd, int yd){
|
||||
// Trova un nodo in cache e lo rimuove sia dalla hash table che dalla lista LRU
|
||||
int index = calculate_cache_hash(xp, yp, xd, yd, cache->size);
|
||||
CacheNode* current = cache->buckets[index];
|
||||
|
||||
while (current != NULL) {
|
||||
if (current->xp == xp && current->yp == yp && current->xd == xd && current->yd == yd) {
|
||||
remove_node(cache, current, xp, yp, xd, yd); // Rimuove sia dalla hash table che dalla lista LRU
|
||||
return;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
|
||||
void clear_cache(CacheHashTable* cache){
|
||||
// Pulisce tutta la cache ma ne mantiene la struttura (per non dover reinizializzare ogni volta la hash table)
|
||||
// NOTA: non faccio la free sugli elementi perchè sono tutti nella pool: verranno sovrascritti dopo
|
||||
memset(cache->buckets, 0, cache->size * sizeof(CacheNode*));
|
||||
cache->pool_index = 0;
|
||||
cache->element_number = 0;
|
||||
|
||||
// Resetta head e tail
|
||||
cache->head = &cache->pool[cache->pool_index++];
|
||||
cache->tail = &cache->pool[cache->pool_index++];
|
||||
cache->head->lru_next = cache->tail;
|
||||
cache->tail->lru_prev = cache->head;
|
||||
cache->head->lru_prev = NULL;
|
||||
cache->tail->lru_next = NULL;
|
||||
}
|
||||
|
||||
void destroy_cache(CacheHashTable** cache){
|
||||
// Elimino completamente la struttura e ripulisco la pool
|
||||
if (*cache) {
|
||||
free((*cache)->buckets);
|
||||
free((*cache)->pool);
|
||||
free(*cache);
|
||||
*cache = NULL;
|
||||
}
|
||||
}
|
||||
// END
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN MAIN
|
||||
int main(){
|
||||
char testo[30];
|
||||
int inp_uno, inp_due, inp_tre, inp_quattro;
|
||||
|
||||
HexMap map;
|
||||
HashTable* ht = NULL;
|
||||
MinHeap* heap = NULL;
|
||||
CacheHashTable* cache = NULL;
|
||||
|
||||
int already_initialized=0;
|
||||
CacheNode* cached;
|
||||
int cost;
|
||||
|
||||
while (true){
|
||||
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){
|
||||
|
||||
// MAPPA
|
||||
if (already_initialized==1){
|
||||
destroy_map(&map);
|
||||
}
|
||||
init_map(&map, inp_uno, inp_due);
|
||||
already_initialized=1;
|
||||
|
||||
// HASH TABLE
|
||||
if (ht!=NULL){
|
||||
destroy_hash_table(&ht);
|
||||
}
|
||||
ht = create_hash_table(inp_uno * inp_due);
|
||||
|
||||
// HEAP
|
||||
if (heap!=NULL){
|
||||
heap_destroy(heap);
|
||||
}
|
||||
heap = heap_create(inp_uno * inp_due);
|
||||
|
||||
// CACHE
|
||||
if (cache!=NULL){
|
||||
destroy_cache(&cache);
|
||||
}
|
||||
cache = create_cache(CACHE_SIZE);
|
||||
}
|
||||
|
||||
|
||||
if (strcmp(testo, "print")==0){
|
||||
if (already_initialized==0){
|
||||
fprintf(stdout, "-1\n");
|
||||
fflush(stdout);
|
||||
continue;
|
||||
}
|
||||
print_map(&map);
|
||||
}
|
||||
|
||||
|
||||
if (strcmp(testo, "change_cost")==0){
|
||||
if (already_initialized==0){
|
||||
fprintf(stdout, "-1\n");
|
||||
fflush(stdout);
|
||||
continue;
|
||||
}
|
||||
if(change_cost(&map, inp_uno, inp_due, inp_tre, inp_quattro)){
|
||||
clear_cache(cache);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (strcmp(testo, "toggle_air_route")==0){
|
||||
if (already_initialized==0){
|
||||
fprintf(stdout, "-1\n");
|
||||
fflush(stdout);
|
||||
continue;
|
||||
}
|
||||
if(toggle_air_route(&map, inp_uno, inp_due, inp_tre, inp_quattro)){
|
||||
clear_cache(cache);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (strcmp(testo, "travel_cost")==0){
|
||||
if (already_initialized==0){
|
||||
fprintf(stdout, "-1\n");
|
||||
fflush(stdout);
|
||||
continue;
|
||||
}
|
||||
cached=cache_lookup(cache, inp_uno, inp_due, inp_tre, inp_quattro);
|
||||
|
||||
if(cached!=NULL){
|
||||
cost = cached->cost;
|
||||
} else {
|
||||
cost = travel_cost(&map, ht, heap, inp_uno, inp_due, inp_tre, inp_quattro);
|
||||
cache_insert(cache, inp_uno, inp_due, inp_tre, inp_quattro, cost);
|
||||
}
|
||||
|
||||
fprintf(stdout, "%d\n", cost);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
if (already_initialized==1) {
|
||||
destroy_map(&map);
|
||||
}
|
||||
if (ht!=NULL) {
|
||||
destroy_hash_table(&ht);
|
||||
}
|
||||
if (heap!=NULL) {
|
||||
heap_destroy(heap);
|
||||
}
|
||||
if (cache!=NULL) {
|
||||
destroy_cache(&cache);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
// END
|
||||
@@ -0,0 +1,865 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <math.h>
|
||||
|
||||
#define CACHE_SIZE 4096
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN STRUCTURES
|
||||
typedef struct Node { // Nodo puntato sia dalla hash table che dall'heap. Usato in Dijkstra
|
||||
int x, y; // Coordinate
|
||||
int cost; // Costo di raggiungimento
|
||||
int heap_index; // Indice nell'heap
|
||||
struct Node* next; // Puntatore al prossimo elemento (hash table con chaining)
|
||||
} Node;
|
||||
|
||||
typedef struct { // Hash table
|
||||
int size; // Quantità di nodi presenti
|
||||
int capacity; // Capacità massima
|
||||
Node** buckets; // Puntatore all'array di puntatori ai Node
|
||||
Node* pool; // Puntatore alla pool di memoria contigua
|
||||
int pool_index; // Indice del pool di memoria contigua
|
||||
} HashTable;
|
||||
|
||||
typedef struct MinHeap { // Heap
|
||||
int size; // Quantità di nodi presenti
|
||||
int capacity; // Capacità massima
|
||||
Node** queue; // Puntatore all'array di puntatori ai Node
|
||||
} MinHeap;
|
||||
|
||||
typedef struct { // Rotta aerea
|
||||
int dest_x, dest_y; // Destinazione
|
||||
int cost; // Costo di raggiungimento
|
||||
} AirRoute;
|
||||
|
||||
typedef struct { // Esagono
|
||||
int land_cost; // Costo uscita esagono
|
||||
AirRoute air_routes[5]; // Array di rotte aeree
|
||||
int air_route_count; // Quantità di rotte aeree presenti
|
||||
} Hexagon;
|
||||
|
||||
typedef struct { // Mappa di esagoni
|
||||
int rows, cols; // Numero di righe e colonne
|
||||
Hexagon* grid_data; // Puntatore al blocco in RAM contenente tutti gli esagoni
|
||||
Hexagon** grid; // Array di puntatori alle colonne
|
||||
} HexMap;
|
||||
|
||||
typedef struct CacheNode { // Nodo della cache
|
||||
int xp, yp; // Coordinate di partenza
|
||||
int xd, yd; // Coordinate di arrivo
|
||||
int cost; // Costo di percorrenza
|
||||
struct CacheNode* next; // Puntatore al prossimo elemento (hash table chained)
|
||||
struct CacheNode* lru_prev; // Puntatore all'elemento precedente nella double linked list LRU
|
||||
struct CacheNode* lru_next; // Puntatore al prossimo elemento nella double linked list LRU
|
||||
} CacheNode;
|
||||
|
||||
typedef struct { // Hash table della cache
|
||||
int size; // Quantità di bucket
|
||||
int capacity; // Quantità massima di nodi prima di iniziare ad attuare la policy di LRU
|
||||
int element_number; // Quantità attuale di elementi nella cache
|
||||
CacheNode** buckets; // Puntatore all'array di puntatori ai CacheNode
|
||||
CacheNode* pool; // Puntatore alla pool di memoria contigua
|
||||
int pool_index; // Indice del pool di memoria contigua
|
||||
CacheNode* head; // CacheNode più recentemente usato
|
||||
CacheNode* tail; // CacheNode meno recentemente usato
|
||||
} CacheHashTable;
|
||||
// END
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN MAP FUNCTIONS
|
||||
inline void init_map(HexMap* map, int cols, int rows){
|
||||
// Inizializza la griglia: prende in input il puntatore alla struttura mappa, righe e colonne e crea la griglia formata da un array di puntatori ad array
|
||||
map->rows = rows;
|
||||
map->cols = cols;
|
||||
|
||||
// Alloca lo spazio di tutti gli esagoni in blocco (per minimizzare cache miss)
|
||||
map->grid_data = (Hexagon*) calloc(cols * rows, sizeof(Hexagon));
|
||||
|
||||
// Alloca i puntatori alle singole colonne
|
||||
map->grid = (Hexagon**) malloc(cols * sizeof(Hexagon*));
|
||||
|
||||
// Inizializza i puntatori alle singole colonne
|
||||
for (int i = 0; i < cols; i++) {
|
||||
map->grid[i] = &map->grid_data[i * rows];
|
||||
}
|
||||
|
||||
// Inizializza gli attributi di tutti gli esagoni (la calloc mette tutto a zero, quindi l'air_route_count è già a 0)
|
||||
for (int i = 0; i < cols * rows; i++) {
|
||||
map->grid_data[i].land_cost = 1;
|
||||
}
|
||||
|
||||
fprintf(stdout,"OK\n");
|
||||
}
|
||||
|
||||
void destroy_map(HexMap* map){
|
||||
// Distrugge la mappa
|
||||
free(map->grid_data); // Dealloca il mega blocco contenente tutti gli esagoni
|
||||
free(map->grid); // Dealloca i singoli puntatori alle colonne
|
||||
map->grid = NULL;
|
||||
map->grid_data = NULL;
|
||||
map->rows = map->cols = 0;
|
||||
}
|
||||
|
||||
inline void print_map(HexMap* map){
|
||||
// Stampa la mappa
|
||||
printf("\n");
|
||||
for (int i=map->rows-1; i>=0; i--){
|
||||
if(i%2==1) printf(" ");
|
||||
for (int j=0; j<map->cols; j++){
|
||||
printf("%d ",map->grid[j][i].land_cost);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
inline bool is_valid(HexMap* map, int x, int y){
|
||||
// Controlla se delle coordinate sono entro i bordi della mappa
|
||||
return !(x<0 || x>map->cols-1 || y<0 || y>map->rows-1);
|
||||
}
|
||||
|
||||
inline int hexagons_distance(int x_1, int y_1, int x_2, int y_2){
|
||||
// Calcola la distanza tra due esagoni tramite le coordinate cubiche (non ho idea di come funzioni, fidati)
|
||||
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)));
|
||||
}
|
||||
|
||||
bool change_cost(HexMap* map, int x, int y, int cost, int radius){
|
||||
// Cambia il costo nella mappa: per ogni esagono nella mappa all'interno del quadrato che inscrive il cerchio di raggio radius calcola la distanza dal nodo sorgente, se è inferiore del raggio allora cambia il costo dell'esagono secondo la formula. Successivamente aggiorna i costi delle rotte aeree
|
||||
// NOTA: il costo può variare massimo tra 0 e 100
|
||||
if(!is_valid(map, x, y) || radius<=0 || cost < -10 || cost > 10){
|
||||
fputs("KO\n", stdout);
|
||||
return false;
|
||||
}
|
||||
|
||||
int dist;
|
||||
int cost_old_air_route;
|
||||
float coeff;
|
||||
|
||||
// Le x e le y che vado a modificare sono solo quelle dentro al quadrato che inscrive la circonferenza di raggio radius
|
||||
int min_x = fmax(0, x - radius);
|
||||
int max_x = fmin(map->cols - 1, x + radius);
|
||||
int min_y = fmax(0, y - radius);
|
||||
int max_y = fmin(map->rows - 1, y + radius);
|
||||
|
||||
for(int i = min_x; i <= max_x; i++){
|
||||
for (int j = min_y; j <= max_y; j++){
|
||||
dist = hexagons_distance(x, y, i, j);
|
||||
if (dist < radius){
|
||||
coeff = fmax(0.0f, (float)(radius - dist) / (float)radius); // Aggiorna il costo via terra
|
||||
Hexagon* hex = &map->grid[i][j];
|
||||
hex->land_cost = hex->land_cost + (int)floor(cost * coeff);
|
||||
|
||||
if (hex->land_cost > 100){ // Impone che il costo via terra sia compreso tra 0 e 100
|
||||
hex->land_cost = 100;
|
||||
}
|
||||
if (hex->land_cost < 0){
|
||||
hex->land_cost = 0;
|
||||
}
|
||||
|
||||
for (int counter = 0; counter < hex->air_route_count; counter++){ // Aggiorna il costo delle rotte aeree
|
||||
cost_old_air_route = 0;
|
||||
for (int n = 0; n < counter; n++){
|
||||
cost_old_air_route = cost_old_air_route + hex->air_routes[n].cost;
|
||||
}
|
||||
hex->air_routes[counter].cost = (int)((cost_old_air_route + hex->land_cost) / (counter + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fputs("OK\n", stdout);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool toggle_air_route(HexMap* map, int x_1, int y_1, int x_2, int y_2){
|
||||
// Inserisce o rimuove una rotta aerea da un esagono
|
||||
if(!is_valid(map, x_1,y_1) || !is_valid(map, x_2,y_2)){
|
||||
fputs("KO\n", stdout);
|
||||
return false;
|
||||
}
|
||||
|
||||
Hexagon* StartingHexagon = &(map->grid[x_1][y_1]);
|
||||
|
||||
if (StartingHexagon->air_route_count>4){
|
||||
fputs("KO\n", stdout);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i=0; i< StartingHexagon->air_route_count; i++){
|
||||
if ((StartingHexagon->air_routes[i].dest_x == x_2)&&(StartingHexagon->air_routes[i].dest_y == y_2)){
|
||||
for (int j=i; j<StartingHexagon->air_route_count; j++){
|
||||
StartingHexagon->air_routes[j]=StartingHexagon->air_routes[j+1];
|
||||
}
|
||||
StartingHexagon->air_route_count = StartingHexagon->air_route_count - 1;
|
||||
fputs("OK\n", stdout);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
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};
|
||||
StartingHexagon->air_route_count = StartingHexagon->air_route_count + 1;
|
||||
fputs("OK\n", stdout);
|
||||
return true;
|
||||
}
|
||||
// END
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN HASH TABLE FUNCTIONS
|
||||
inline int calculate_hash(int x, int y, int size){
|
||||
// Calcola l'hash dati in input le coordinate e la dimensione della hash table
|
||||
return ((x * 73 + y * 31) % size);
|
||||
}
|
||||
|
||||
inline HashTable* create_hash_table(int size){
|
||||
// Genera l'hash table con fattore di carico 1.5 per bilanciare performance temporali e spaziali. Calcola il numero primo migliore da usare come dimensione dell'hash table e poi inizializza tutti i suoi attributi
|
||||
// NOTA: con calloc() vado a inizializzare già tutti i bucket a 0, quindi non devo preoccuparmi di farli puntare a NULL
|
||||
int min_size = (int)ceil(size / 1.3);
|
||||
|
||||
int hash_length;
|
||||
if (min_size <= 53) {
|
||||
hash_length = 53;
|
||||
} else if (min_size <= 97) {
|
||||
hash_length = 97;
|
||||
} else if (min_size <= 193) {
|
||||
hash_length = 193;
|
||||
} else if (min_size <= 389) {
|
||||
hash_length = 389;
|
||||
} else if (min_size <= 769) {
|
||||
hash_length = 769;
|
||||
} else if (min_size <= 1543) {
|
||||
hash_length = 1543;
|
||||
} else if (min_size <= 3079) {
|
||||
hash_length = 3079;
|
||||
} else if (min_size <= 6151) {
|
||||
hash_length = 6151;
|
||||
} else if (min_size <= 12289) {
|
||||
hash_length = 12289;
|
||||
} else if (min_size <= 24593) {
|
||||
hash_length = 24593;
|
||||
} else if (min_size <= 49157) {
|
||||
hash_length = 49157;
|
||||
} else if (min_size <= 98317) {
|
||||
hash_length = 98317;
|
||||
} else if (min_size <= 196613) {
|
||||
hash_length = 196613;
|
||||
} else if (min_size <= 393241) {
|
||||
hash_length = 393241;
|
||||
} else if (min_size <= 786433) {
|
||||
hash_length = 786433;
|
||||
} else {
|
||||
hash_length = 1572869;
|
||||
}
|
||||
|
||||
HashTable* ht = (HashTable*) malloc(sizeof(HashTable));
|
||||
ht->size = hash_length;
|
||||
ht->capacity = size;
|
||||
ht->buckets = calloc(hash_length, sizeof(Node*));
|
||||
ht->pool = malloc(size * sizeof(Node));
|
||||
ht->pool_index = 0;
|
||||
|
||||
return ht;
|
||||
}
|
||||
|
||||
inline void clear_hash_table(HashTable* ht){
|
||||
// Mette a 0 tutti i bucket e va a resettare il pool di memoria contigua
|
||||
// NOTA: non va a fare delle free perchè al prossimo utilizzo della hash table vado a sovrascrivere i nodi (verranno sempre scritti nel pool di memoria)
|
||||
memset(ht->buckets, 0, ht->size * sizeof(Node*));
|
||||
ht->pool_index = 0;
|
||||
}
|
||||
|
||||
void destroy_hash_table(HashTable** ht){
|
||||
// Elimina completamente una hash table (anche il suo pool di memoria contigua)
|
||||
if (*ht) {
|
||||
free((*ht)->buckets);
|
||||
free((*ht)->pool);
|
||||
free(*ht);
|
||||
*ht = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
inline Node* insert_or_update_element(HashTable* ht, int x, int y, int cost){
|
||||
// Se il nodo che sto provando ad inserire non esiste, lo inserisco in testa alla chain.
|
||||
// Se invece esiste controllo il costo: se è maggiore di quello già presente in hash table ignoro (ritorno NULL), altrimenti aggiorno il costo
|
||||
// NOTA: quando inserisco per la prima volta in hash table inizializzo heap_index a -1 per intendere che non è ancora stato inserito in heap
|
||||
int index = calculate_hash(x, y, ht->size);
|
||||
|
||||
Node* current = ht->buckets[index];
|
||||
while (current!=NULL) {
|
||||
if (current->x == x && current->y == y) {
|
||||
if (current->cost > cost) {
|
||||
current->cost = cost;
|
||||
return current;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
if (ht->pool_index >= ht->capacity){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Node* new_node = &ht->pool[ht->pool_index++];
|
||||
new_node->x = x;
|
||||
new_node->y = y;
|
||||
new_node->cost = cost;
|
||||
new_node->heap_index = -1;
|
||||
new_node->next = ht->buckets[index];
|
||||
ht->buckets[index] = new_node;
|
||||
|
||||
return new_node;
|
||||
}
|
||||
// END
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN HEAP FUNCTIONS
|
||||
inline void heap_swap(Node** a, Node** b){
|
||||
// Permette di swappare due nodi (viene usato dalle heapify)
|
||||
Node* temp = *a;
|
||||
*a = *b;
|
||||
*b = temp;
|
||||
|
||||
int tmp_index = (*a)->heap_index;
|
||||
(*a)->heap_index = (*b)->heap_index;
|
||||
(*b)->heap_index = tmp_index;
|
||||
}
|
||||
|
||||
inline void heapify_up(MinHeap* heap, int index){
|
||||
// Fa salire un nodo dal basso verso l'alto (lo swappo con il genitore)
|
||||
// NOTA: viene usato quando inserisco un nuovo nodo: lo inserisco come foglia e poi lo faccio risalire fino alla sua posizione corretta
|
||||
int parent;
|
||||
while (index > 0) {
|
||||
parent = (index - 1) / 2;
|
||||
if (heap->queue[index]->cost >= heap->queue[parent]->cost){
|
||||
break;
|
||||
}
|
||||
|
||||
heap_swap(&heap->queue[index], &heap->queue[parent]);
|
||||
index = parent;
|
||||
}
|
||||
}
|
||||
|
||||
inline void heapify_down(MinHeap* heap, int index){
|
||||
// Fa scendere un nodo dall'alto verso il basso (lo swappo con un figlio)
|
||||
// NOTA: viene usato quando consumo il nodo minimo (root) in Dijkstra: metto il nodo più grande di tutti come root e poi lo faccio scendere fino alla posizione corretta
|
||||
int left, right, smallest;
|
||||
int size = heap->size;
|
||||
|
||||
while (true) {
|
||||
left = (index * 2) + 1;
|
||||
right = left + 1;
|
||||
smallest = index;
|
||||
|
||||
if (left < size && heap->queue[left]->cost < heap->queue[smallest]->cost) {
|
||||
smallest = left;
|
||||
}
|
||||
|
||||
if (right < size && heap->queue[right]->cost < heap->queue[smallest]->cost) {
|
||||
smallest = right;
|
||||
}
|
||||
|
||||
if (smallest == index){
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
heap_swap(&heap->queue[index], &heap->queue[smallest]);
|
||||
index = smallest;
|
||||
}
|
||||
}
|
||||
|
||||
inline void heap_enqueue(MinHeap* heap, Node* node){
|
||||
// Se il nodo non è già presente in heap (index==-1) allora lo aggiungo come foglia e poi lo faccio risalire.
|
||||
// Se invece il nodo esiste già, gli aggiorno il costo e poi lo faccio risalire (il nuovo costo è per forza minore, quindi deve salire)
|
||||
// NOTA: sono sicuro che il costo nuovo sia inferiore del precedente perchè chiamo l'heap_enqueue solo dopo aver controllato tramite la hash table
|
||||
if (node->heap_index == -1) {
|
||||
if(heap->size >= heap->capacity){
|
||||
return;
|
||||
}
|
||||
|
||||
node->heap_index = heap->size;
|
||||
heap->queue[heap->size] = node;
|
||||
heap->size++;
|
||||
|
||||
heapify_up(heap, node->heap_index);
|
||||
} else {
|
||||
heapify_up(heap, node->heap_index);
|
||||
}
|
||||
}
|
||||
|
||||
inline Node* heap_dequeue(MinHeap* heap){
|
||||
// Consumo il primo nodo della heap: gli imposto l'index a -1 per intendere che non è più in heap e poi lo ritorno
|
||||
// NOTA: per sistemare l'heap metto come root il nodo più grande (quello all'ultimo indice) in root e poi lo faccio scendere
|
||||
if (heap->size == 0){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Node* min = heap->queue[0];
|
||||
min->heap_index = -1;
|
||||
|
||||
heap->size--;
|
||||
|
||||
if (heap->size > 0) {
|
||||
heap->queue[0] = heap->queue[heap->size];
|
||||
heap->queue[0]->heap_index = 0;
|
||||
heapify_down(heap, 0);
|
||||
}
|
||||
|
||||
return min;
|
||||
}
|
||||
|
||||
inline MinHeap* heap_create(int capacity){
|
||||
// Crea l'heap'
|
||||
MinHeap* heap = malloc(sizeof(MinHeap));
|
||||
heap->queue = malloc(sizeof(Node*) * capacity);
|
||||
heap->size = 0;
|
||||
heap->capacity = capacity;
|
||||
return heap;
|
||||
}
|
||||
|
||||
inline void heap_clear(MinHeap* heap){
|
||||
// Imposta semplicemente la dimensione dell'heap a 0, tanto ai successivi usi vado a sovrascrivere i puntatori che sono presenti
|
||||
heap->size = 0;
|
||||
}
|
||||
|
||||
void heap_destroy(MinHeap* heap){
|
||||
// Vado ad eliminare completamente l'heap
|
||||
free(heap->queue);
|
||||
free(heap);
|
||||
}
|
||||
// END
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN DIJKSTRA
|
||||
int travel_cost(HexMap* map, HashTable* ht, MinHeap* heap, int xp, int yp, int xd, int yd) {
|
||||
// Inserisco il nodo sorgente in hash table e in heap.
|
||||
// Nel while (che va avanti finchè non esaurisco i nodi nell'heap) faccio:
|
||||
// - Prendo il primo elemento dall'heap (quindi il minimo)
|
||||
// - Se è il nodo destinazione, ritorno il costo di raggiungimento (non mi serve esplorare ulteriormente per come è fatto Dijkstra, appena lo trovo ho già trovato il costo minore)
|
||||
// - Controllo i 6 nodi vicini e, se il loro costo di raggiungimento è minore di quello che già hanno in hash table (oppure se vengono inseriti per la prima volta in hash table), vado ad inserirli anche in heap
|
||||
// - Controllo tutte le rotte aeree del nodo e, se il nodo di destinazione ha costo di raggiungimento minore di quello già presente in hash table, lo inserisco in heap
|
||||
// Controllo alla fine il costo di raggiungimento del nodo di destinazione (teoricamente non dovrei mai arrivarci qui)
|
||||
// Pulisco heap e hash table
|
||||
|
||||
|
||||
// Controllo la validità delle coordinate (non so perchè ma is_valid non funziona)
|
||||
if ((unsigned)xp >= map->cols || (unsigned)yp >= map->rows || (unsigned)xd >= map->cols || (unsigned)yd >= map->rows) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Esco se partenza e destinazione coincidono
|
||||
if (xp == xd && yp == yd){
|
||||
return 0;
|
||||
}
|
||||
|
||||
Node* new_node = insert_or_update_element(ht, xp, yp, 0);
|
||||
heap_enqueue(heap, new_node);
|
||||
|
||||
const int cols = map->cols;
|
||||
const int rows = map->rows;
|
||||
|
||||
static const int dir_even[6][2] = {
|
||||
{+1, 0}, { 0, -1}, {+1, -1},
|
||||
{-1, 0}, {+1, +1}, { 0, +1}
|
||||
};
|
||||
static const int dir_odd[6][2] = {
|
||||
{+1, 0}, {-1, -1}, { 0, -1},
|
||||
{-1, 0}, { 0, +1}, {-1, +1}
|
||||
};
|
||||
|
||||
while (heap->size > 0) {
|
||||
Node* current = heap_dequeue(heap);
|
||||
|
||||
// Se current==destinazione
|
||||
if (current->x == xd && current->y == yd) {
|
||||
int result = current->cost;
|
||||
heap_clear(heap);
|
||||
clear_hash_table(ht);
|
||||
return result;
|
||||
}
|
||||
|
||||
Hexagon* hex = &map->grid[current->x][current->y];
|
||||
const int land_cost = hex->land_cost;
|
||||
|
||||
if (land_cost == 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
// Scelgo i vicini in base alla parità
|
||||
const int (*dirs)[2] = (current->y & 1) ? dir_even : dir_odd;
|
||||
int new_cost = current->cost + land_cost;
|
||||
|
||||
// Controllo i 6 vicini
|
||||
for (int i = 0; i < 6; i++) {
|
||||
int new_x = current->x + dirs[i][0];
|
||||
int new_y = current->y + dirs[i][1];
|
||||
|
||||
if ((unsigned)new_x < cols && (unsigned)new_y < rows) {
|
||||
new_node = insert_or_update_element(ht, new_x, new_y, new_cost);
|
||||
if (new_node) {
|
||||
heap_enqueue(heap, new_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Controllo le rotte aeree
|
||||
int route_count = hex->air_route_count;
|
||||
for (int i = 0; i < route_count; i++) {
|
||||
new_node = insert_or_update_element(ht, hex->air_routes[i].dest_x, hex->air_routes[i].dest_y, current->cost + hex->air_routes[i].cost);
|
||||
if (new_node) {
|
||||
heap_enqueue(heap, new_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Destination not reached
|
||||
heap_clear(heap);
|
||||
clear_hash_table(ht);
|
||||
return -1;
|
||||
}
|
||||
// END
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN CACHE FUNCTIONS
|
||||
inline int calculate_cache_hash(int xp, int yp, int xd, int yd, int size){
|
||||
// Calcola l'hash dati in input le quattro coordinate e la dimensione della hash table della cache
|
||||
return ((xp * 1009 + yp * 1013 + xd * 1019 + yd * 1021) % size);
|
||||
}
|
||||
|
||||
inline CacheHashTable* create_cache(int capacity){
|
||||
// Crea la cache con un fattore di carico di circa 1.2 (per massimizzare efficienza temporale e spaziale).
|
||||
// Inizializza tutti gli attributi della tabella hash stessa (dimensione, capacità, pool ecc...) e e anche della lista doppiamente concatenata LRU (head e tail)
|
||||
int min_size = (int)ceil(capacity / 1.2);
|
||||
|
||||
int hash_length;
|
||||
if (min_size <= 53){
|
||||
hash_length = 53;
|
||||
} else if (min_size <= 97){
|
||||
hash_length = 97;
|
||||
} else if (min_size <= 193){
|
||||
hash_length = 193;
|
||||
} else if (min_size <= 389){
|
||||
hash_length = 389;
|
||||
} else if (min_size <= 769){
|
||||
hash_length = 769;
|
||||
} else if (min_size <= 1543){
|
||||
hash_length = 1543;
|
||||
} else if (min_size <= 3079){
|
||||
hash_length = 3079;
|
||||
} else{
|
||||
hash_length = 6151;
|
||||
}
|
||||
|
||||
|
||||
// Hash table
|
||||
CacheHashTable* cache = (CacheHashTable*)malloc(sizeof(CacheHashTable));
|
||||
cache->size = hash_length;
|
||||
cache->capacity = capacity;
|
||||
cache->element_number = 0;
|
||||
cache->buckets = calloc(hash_length, sizeof(CacheNode*));
|
||||
cache->pool = malloc(capacity * sizeof(CacheNode));
|
||||
cache->pool_index = 0;
|
||||
|
||||
// Lista LRU
|
||||
cache->head = &cache->pool[cache->pool_index++];
|
||||
cache->tail = &cache->pool[cache->pool_index++];
|
||||
cache->head->lru_next = cache->tail;
|
||||
cache->tail->lru_prev = cache->head;
|
||||
cache->head->lru_prev = NULL;
|
||||
cache->tail->lru_next = NULL;
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
inline void lru_move_to_head(CacheHashTable* cache, CacheNode* node){
|
||||
// Prende in ingresso un nodo già presente in lista e lo inserisce in testa (quando faccio una lookup deve andare in testa)
|
||||
|
||||
// Gestisce i puntatori prima di spostare il nodo
|
||||
if (node->lru_prev){
|
||||
node->lru_prev->lru_next = node->lru_next;
|
||||
}
|
||||
if (node->lru_next){
|
||||
node->lru_next->lru_prev = node->lru_prev;
|
||||
}
|
||||
|
||||
// Sposta il nodo in testa
|
||||
node->lru_next = cache->head->lru_next;
|
||||
node->lru_prev = cache->head;
|
||||
cache->head->lru_next->lru_prev = node;
|
||||
cache->head->lru_next = node;
|
||||
}
|
||||
|
||||
inline void remove_node(CacheHashTable* cache, CacheNode* node, int xp, int yp, int xd, int yd){
|
||||
// Rimuove un nodo sia dalla lista che dalla hash table
|
||||
|
||||
// Rimozione dalla hash table
|
||||
int index = calculate_cache_hash(xp, yp, xd, yd, cache->size);
|
||||
CacheNode* current = cache->buckets[index];
|
||||
CacheNode* prev = NULL;
|
||||
|
||||
while (current != NULL) {
|
||||
if (current == node) {
|
||||
if (prev == NULL) {
|
||||
cache->buckets[index] = current->next;
|
||||
} else {
|
||||
prev->next = current->next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
prev = current;
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
// Rimozione dalla lista LRU
|
||||
if (node->lru_prev) node->lru_prev->lru_next = node->lru_next;
|
||||
if (node->lru_next) node->lru_next->lru_prev = node->lru_prev;
|
||||
|
||||
cache->element_number--;
|
||||
}
|
||||
|
||||
inline CacheNode* cache_lookup(CacheHashTable* cache, int xp, int yp, int xd, int yd){
|
||||
// Cerca un nodo in cache e, se lo trova, lo muove in testa nella lista LRU
|
||||
int index = calculate_cache_hash(xp, yp, xd, yd, cache->size);
|
||||
CacheNode* current = cache->buckets[index];
|
||||
|
||||
while (current != NULL) {
|
||||
if (current->xp == xp && current->yp == yp && current->xd == xd && current->yd == yd) { // Se viene trovato il nodo
|
||||
lru_move_to_head(cache, current);
|
||||
return current;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
return NULL; // Se non viene trovato, restituisco NULL
|
||||
}
|
||||
|
||||
inline CacheNode* cache_insert(CacheHashTable* cache, int xp, int yp, int xd, int yd, int cost){
|
||||
// Cerca nell'hash table se il nodo esiste già (nella lookup c'è già lo spostamento in testa). Nel caso aggiorna il costo
|
||||
CacheNode* existing = cache_lookup(cache, xp, yp, xd, yd);
|
||||
if (existing != NULL) {
|
||||
existing->cost = cost;
|
||||
return existing;
|
||||
}
|
||||
|
||||
// Se la cache è piena, rimuovo il nodo in coda
|
||||
if (cache->element_number >= cache->capacity) {
|
||||
CacheNode* lru_node = cache->tail->lru_prev;
|
||||
if (lru_node != cache->head) {
|
||||
remove_node(cache, lru_node, lru_node->xp, lru_node->yp, lru_node->xd, lru_node->yd);
|
||||
}
|
||||
}
|
||||
|
||||
// Arrivo qua solamente se il nodo non è già presente in cache; creo il nodo
|
||||
CacheNode* new_node = &cache->pool[cache->pool_index++];
|
||||
new_node->xp = xp;
|
||||
new_node->yp = yp;
|
||||
new_node->xd = xd;
|
||||
new_node->yd = yd;
|
||||
new_node->cost = cost;
|
||||
|
||||
// Lo aggiungo in hash table
|
||||
int index = calculate_cache_hash(xp, yp, xd, yd, cache->size);
|
||||
new_node->next = cache->buckets[index];
|
||||
cache->buckets[index] = new_node;
|
||||
|
||||
// Lo aggiungo in testa alla coda
|
||||
new_node->lru_next = cache->head->lru_next;
|
||||
new_node->lru_prev = cache->head;
|
||||
cache->head->lru_next->lru_prev = new_node;
|
||||
cache->head->lru_next = new_node;
|
||||
|
||||
cache->element_number++;
|
||||
return new_node;
|
||||
}
|
||||
|
||||
inline void cache_remove(CacheHashTable* cache, int xp, int yp, int xd, int yd){
|
||||
// Trova un nodo in cache e lo rimuove sia dalla hash table che dalla lista LRU
|
||||
int index = calculate_cache_hash(xp, yp, xd, yd, cache->size);
|
||||
CacheNode* current = cache->buckets[index];
|
||||
|
||||
while (current != NULL) {
|
||||
if (current->xp == xp && current->yp == yp && current->xd == xd && current->yd == yd) {
|
||||
remove_node(cache, current, xp, yp, xd, yd); // Rimuove sia dalla hash table che dalla lista LRU
|
||||
return;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
|
||||
void clear_cache(CacheHashTable* cache){
|
||||
// Pulisce tutta la cache ma ne mantiene la struttura (per non dover reinizializzare ogni volta la hash table)
|
||||
// NOTA: non faccio la free sugli elementi perchè sono tutti nella pool: verranno sovrascritti dopo
|
||||
memset(cache->buckets, 0, cache->size * sizeof(CacheNode*));
|
||||
cache->pool_index = 0;
|
||||
cache->element_number = 0;
|
||||
|
||||
// Resetta head e tail
|
||||
cache->head = &cache->pool[cache->pool_index++];
|
||||
cache->tail = &cache->pool[cache->pool_index++];
|
||||
cache->head->lru_next = cache->tail;
|
||||
cache->tail->lru_prev = cache->head;
|
||||
cache->head->lru_prev = NULL;
|
||||
cache->tail->lru_next = NULL;
|
||||
}
|
||||
|
||||
void destroy_cache(CacheHashTable** cache){
|
||||
// Elimino completamente la struttura e ripulisco la pool
|
||||
if (*cache) {
|
||||
free((*cache)->buckets);
|
||||
free((*cache)->pool);
|
||||
free(*cache);
|
||||
*cache = NULL;
|
||||
}
|
||||
}
|
||||
// END
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN MAIN
|
||||
int main(){
|
||||
|
||||
|
||||
char testo[17];
|
||||
int inp_uno, inp_due, inp_tre, inp_quattro;
|
||||
|
||||
HexMap map;
|
||||
HashTable* ht = NULL;
|
||||
MinHeap* heap = NULL;
|
||||
CacheHashTable* cache = NULL;
|
||||
|
||||
int already_initialized=0;
|
||||
CacheNode* cached;
|
||||
int cost;
|
||||
|
||||
while (true){
|
||||
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 (heap!=NULL){
|
||||
heap_destroy(heap);
|
||||
}
|
||||
heap = heap_create(inp_uno * inp_due);
|
||||
|
||||
if (cache!=NULL){
|
||||
destroy_cache(&cache);
|
||||
}
|
||||
cache = create_cache(CACHE_SIZE);
|
||||
}
|
||||
|
||||
if (strcmp(testo, "print")==0){
|
||||
if (already_initialized==0){
|
||||
fprintf(stdout, "-1\n");
|
||||
continue;
|
||||
}
|
||||
print_map(&map);
|
||||
}
|
||||
|
||||
if (strcmp(testo, "change_cost")==0){
|
||||
if (already_initialized==0){
|
||||
fprintf(stdout, "-1\n");
|
||||
continue;
|
||||
}
|
||||
if(change_cost(&map, inp_uno, inp_due, inp_tre, inp_quattro)){
|
||||
clear_cache(cache);
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(testo, "toggle_air_route")==0){
|
||||
if (already_initialized==0){
|
||||
fprintf(stdout, "-1\n");
|
||||
continue;
|
||||
}
|
||||
if(toggle_air_route(&map, inp_uno, inp_due, inp_tre, inp_quattro)){
|
||||
clear_cache(cache);
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(testo, "travel_cost")==0){
|
||||
if (already_initialized==0){
|
||||
fprintf(stdout, "-1\n");
|
||||
continue;
|
||||
}
|
||||
cached=cache_lookup(cache, inp_uno, inp_due, inp_tre, inp_quattro);
|
||||
|
||||
if(cached!=NULL){
|
||||
cost = cached->cost;
|
||||
} else {
|
||||
cost = travel_cost(&map, ht, heap, inp_uno, inp_due, inp_tre, inp_quattro);
|
||||
cache_insert(cache, inp_uno, inp_due, inp_tre, inp_quattro, cost);
|
||||
}
|
||||
|
||||
fprintf(stdout, "%d\n", cost);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
if (already_initialized==1) {
|
||||
destroy_map(&map);
|
||||
}
|
||||
if (ht!=NULL) {
|
||||
destroy_hash_table(&ht);
|
||||
}
|
||||
if (heap!=NULL) {
|
||||
heap_destroy(heap);
|
||||
}
|
||||
if (cache!=NULL) {
|
||||
destroy_cache(&cache);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
// END
|
||||
@@ -0,0 +1,965 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <math.h>
|
||||
|
||||
#define CACHE_SIZE 4096
|
||||
#define AIR_ROUTE_HASH_SIZE 1024
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN STRUCTURES
|
||||
typedef struct Node { // Nodo puntato sia dalla hash table che dall'heap. Usato in Dijkstra
|
||||
int x, y; // Coordinate
|
||||
int cost; // Costo di raggiungimento
|
||||
int heap_index; // Indice nell'heap
|
||||
struct Node* next; // Puntatore al prossimo elemento (hash table con chaining)
|
||||
} Node;
|
||||
|
||||
typedef struct { // Hash table
|
||||
int size; // Quantità di nodi presenti
|
||||
int capacity; // Capacità massima
|
||||
Node** buckets; // Puntatore all'array di puntatori ai Node
|
||||
Node* pool; // Puntatore alla pool di memoria contigua
|
||||
int pool_index; // Indice del pool di memoria contigua
|
||||
} HashTable;
|
||||
|
||||
typedef struct MinHeap { // Heap
|
||||
int size; // Quantità di nodi presenti
|
||||
int capacity; // Capacità massima
|
||||
Node** queue; // Puntatore all'array di puntatori ai Node
|
||||
} MinHeap;
|
||||
|
||||
typedef struct { // Rotta aerea
|
||||
int dest_x, dest_y; // Destinazione
|
||||
int cost; // Costo di raggiungimento
|
||||
} AirRoute;
|
||||
|
||||
typedef struct AirRouteNode {
|
||||
int start_x, start_y; // Coordinate di partenza
|
||||
AirRoute routes[5]; // Array di rotte aeree
|
||||
int route_count; // Numero di rotte aeree
|
||||
struct AirRouteNode* next; // Puntatore al prossimo nodo nella hash table (chaining)
|
||||
} AirRouteNode;
|
||||
|
||||
typedef struct {
|
||||
int size; // Quantità di buckets
|
||||
int capacity; // Capacità massima di nodi
|
||||
AirRouteNode** buckets; // Puntatore all'array di puntatori ai AirRouteNode
|
||||
AirRouteNode* pool; // Puntatore alla pool di memoria contigua
|
||||
int pool_index; // Indice del pool di memoria contigua
|
||||
} AirRouteTable;
|
||||
|
||||
typedef struct { // Mappa di esagoni - UPDATED: no more Hexagon structure
|
||||
int rows, cols; // Numero di righe e colonne
|
||||
int* grid_data; // Puntatore al blocco in RAM contenente tutti i costi degli esagoni
|
||||
int** grid; // Array di puntatori alle colonne
|
||||
AirRouteTable* air_routes; // Puntatore alla hash table contenente gli air routes
|
||||
} HexMap;
|
||||
|
||||
typedef struct CacheNode { // Nodo della cache
|
||||
int xp, yp; // Coordinate di partenza
|
||||
int xd, yd; // Coordinate di arrivo
|
||||
int cost; // Costo di percorrenza
|
||||
struct CacheNode* next; // Puntatore al prossimo elemento (hash table chained)
|
||||
struct CacheNode* lru_prev; // Puntatore all'elemento precedente nella double linked list LRU
|
||||
struct CacheNode* lru_next; // Puntatore al prossimo elemento nella double linked list LRU
|
||||
} CacheNode;
|
||||
|
||||
typedef struct { // Hash table della cache
|
||||
int size; // Quantità di bucket
|
||||
int capacity; // Quantità massima di nodi prima di iniziare ad attuare la policy di LRU
|
||||
int element_number; // Quantità attuale di elementi nella cache
|
||||
CacheNode** buckets; // Puntatore all'array di puntatori ai CacheNode
|
||||
CacheNode* pool; // Puntatore alla pool di memoria contigua
|
||||
int pool_index; // Indice del pool di memoria contigua
|
||||
CacheNode* head; // CacheNode più recentemente usato
|
||||
CacheNode* tail; // CacheNode meno recentemente usato
|
||||
} CacheHashTable;
|
||||
// END STRUCTURES
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN AIR ROUTE TABLE FUNCTIONS
|
||||
inline int calculate_air_route_hash(int x, int y, int size) {
|
||||
// Calcola la hash nella hash table delle rotte aeree
|
||||
return ((x * 73 + y * 31) & (size - 1));
|
||||
}
|
||||
|
||||
inline AirRouteTable* create_air_route_table(int capacity) {
|
||||
// Allco la tabella di hash e inizializzo gli attributi
|
||||
AirRouteTable* table = (AirRouteTable*) malloc(sizeof(AirRouteTable));
|
||||
table->size = AIR_ROUTE_HASH_SIZE;
|
||||
table->capacity = capacity;
|
||||
table->buckets = (AirRouteNode**) calloc(AIR_ROUTE_HASH_SIZE, sizeof(AirRouteNode*));
|
||||
table->pool = (AirRouteNode*) malloc(capacity * sizeof(AirRouteNode));
|
||||
table->pool_index = 0;
|
||||
return table;
|
||||
}
|
||||
|
||||
inline void clear_air_route_table(AirRouteTable* table) {
|
||||
// Mette a 0 tutti i bucket e va a resettare il pool di memoria contigua
|
||||
// NOTA: non va a fare delle free perchè al prossimo utilizzo della hash table vado a sovrascrivere i nodi (verranno sempre scritti nel pool di memoria)
|
||||
memset(table->buckets, 0, table->size * sizeof(AirRouteNode*));
|
||||
table->pool_index = 0;
|
||||
}
|
||||
|
||||
void destroy_air_route_table(AirRouteTable** table) {
|
||||
// Elimina completamente una hash table delle rotte aeree (anche il suo pool di memoria contigua)
|
||||
if (*table) {
|
||||
free((*table)->buckets);
|
||||
free((*table)->pool);
|
||||
free(*table);
|
||||
*table = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
inline AirRouteNode* find_air_route_node(AirRouteTable* table, int x, int y) {
|
||||
// Trova il nodo contenente le rotte aeree date le coordinate dell'esagono di partenza
|
||||
int index = calculate_air_route_hash(x, y, table->size);
|
||||
AirRouteNode* current = table->buckets[index];
|
||||
|
||||
while (current != NULL) {
|
||||
if (current->start_x == x && current->start_y == y) {
|
||||
return current;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline void remove_air_route_node_if_empty(AirRouteTable* table, int x, int y) {
|
||||
// Quando un nodo di rotte aeree ha 0 rotte aeree nell'array, vado ad eliminare il nodo dalla hash table
|
||||
int index = calculate_air_route_hash(x, y, table->size);
|
||||
AirRouteNode* current = table->buckets[index];
|
||||
AirRouteNode* prev = NULL;
|
||||
|
||||
while (current != NULL) {
|
||||
if (current->start_x == x && current->start_y == y) {
|
||||
if (current->route_count == 0) {
|
||||
if (prev == NULL) {
|
||||
table->buckets[index] = current->next;
|
||||
} else {
|
||||
prev->next = current->next;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
prev = current;
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
|
||||
bool toggle_air_route_in_node(AirRouteTable* table, HexMap* map, int start_x, int start_y, int dest_x, int dest_y) {
|
||||
// Inserisce o rimuove una rotta aerea da un esagono
|
||||
// NOTA: Ritorna sempre true se va tutto bene (ritorna false invece se ci sono problemi)
|
||||
AirRouteNode* route_node = find_air_route_node(table, start_x, start_y);
|
||||
|
||||
// Controlla se la rotta aerea è già presente e, in caso, la rimuove
|
||||
if (route_node != NULL) {
|
||||
for (int i = 0; i < route_node->route_count; i++){
|
||||
if ((route_node->routes[i].dest_x == dest_x) && (route_node->routes[i].dest_y == dest_y)){
|
||||
// Rimuove la rotta
|
||||
for (int j = i; j < route_node->route_count - 1; j++){
|
||||
route_node->routes[j] = route_node->routes[j+1];
|
||||
}
|
||||
route_node->route_count--;
|
||||
|
||||
// Chiamo la funzione per controllare che ci siano ancora rotte aeree (in caso contrario viene eliminato il nodo)
|
||||
remove_air_route_node_if_empty(table, start_x, start_y);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Controlla se posso aggiungere rotte aeree
|
||||
if (route_node->route_count >= 5){
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Creo il nodo delle rotte aeree (se entro qua è perchè il nodo non esiste già)
|
||||
if (table->pool_index >= table->capacity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int index = calculate_air_route_hash(start_x, start_y, table->size);
|
||||
route_node = &table->pool[table->pool_index++];
|
||||
route_node->start_x = start_x;
|
||||
route_node->start_y = start_y;
|
||||
route_node->route_count = 0;
|
||||
route_node->next = table->buckets[index];
|
||||
table->buckets[index] = route_node;
|
||||
}
|
||||
|
||||
// Aggiorno il nodo delle rotte aeree (se sono qua è perchè esiste già il nodo)
|
||||
int starting_hex_cost = map->grid[start_x][start_y];
|
||||
int cost;
|
||||
if (route_node->route_count == 0){
|
||||
cost = (int) floor(starting_hex_cost / (route_node->route_count + 1));
|
||||
} else {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < route_node->route_count; i++){
|
||||
sum += route_node->routes[i].cost;
|
||||
}
|
||||
cost = (int) floor((sum + starting_hex_cost) / (route_node->route_count + 1));
|
||||
}
|
||||
|
||||
route_node->routes[route_node->route_count] = (AirRoute) {dest_x, dest_y, cost};
|
||||
route_node->route_count++;
|
||||
|
||||
return true;
|
||||
}
|
||||
// END AIR ROUTE TABLE FUNCTIONS
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN MAP FUNCTIONS
|
||||
inline void init_map(HexMap* map, int cols, int rows){
|
||||
// Inizializza la griglia: prende in input il puntatore alla struttura mappa, righe e colonne e crea la griglia formata da un array di puntatori ad array
|
||||
map->rows = rows;
|
||||
map->cols = cols;
|
||||
|
||||
// Alloca lo spazio di tutti i costi degli esagoni in blocco (per minimizzare cache miss)
|
||||
map->grid_data = (int*) calloc(cols * rows, sizeof(int));
|
||||
|
||||
// Alloca i puntatori alle singole colonne
|
||||
map->grid = (int**) malloc(cols * sizeof(int*));
|
||||
|
||||
// Inizializza i puntatori alle singole colonne
|
||||
for (int i = 0; i < cols; i++) {
|
||||
map->grid[i] = &map->grid_data[i * rows];
|
||||
}
|
||||
|
||||
// Inizializza i costi di tutti gli esagoni a 1
|
||||
for (int i = 0; i < cols * rows; i++) {
|
||||
map->grid_data[i] = 1;
|
||||
}
|
||||
|
||||
// Inizializza la hash map delle rotte aeree
|
||||
map->air_routes = create_air_route_table(cols * rows / 10); // Verranno aggiunte massimo un decimo di nodi di rotte aeree
|
||||
|
||||
fprintf(stdout,"OK\n");
|
||||
}
|
||||
|
||||
void destroy_map(HexMap* map){
|
||||
// Distrugge la mappa
|
||||
free(map->grid_data); // Dealloca il mega blocco contenente tutti gli esagoni
|
||||
free(map->grid); // Dealloca i singoli puntatori alle colonne
|
||||
destroy_air_route_table(&map->air_routes); // Dealloca la hash table di rotte aeree
|
||||
map->grid = NULL;
|
||||
map->grid_data = NULL;
|
||||
map->air_routes = NULL;
|
||||
map->rows = map->cols = 0;
|
||||
}
|
||||
|
||||
inline void print_map(HexMap* map){
|
||||
// Stampa la mappa
|
||||
printf("\n");
|
||||
for (int i=map->rows-1; i>=0; i--){
|
||||
if(i%2==1) printf(" ");
|
||||
for (int j=0; j<map->cols; j++){
|
||||
printf("%d ",map->grid[j][i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
inline bool is_valid(HexMap* map, int x, int y){
|
||||
// Controlla se delle coordinate sono entro i bordi della mappa
|
||||
return !(x<0 || x>map->cols-1 || y<0 || y>map->rows-1);
|
||||
}
|
||||
|
||||
inline int hexagons_distance(int x_1, int y_1, int x_2, int y_2){
|
||||
// Calcola la distanza tra due esagoni tramite le coordinate cubiche (non ho idea di come funzioni, fidati)
|
||||
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)));
|
||||
}
|
||||
|
||||
bool change_cost(HexMap* map, int x, int y, int cost, int radius){
|
||||
// Cambia il costo nella mappa: per ogni esagono nella mappa all'interno del quadrato che inscrive il cerchio di raggio radius calcola la distanza dal nodo sorgente, se è inferiore del raggio allora cambia il costo dell'esagono secondo la formula. Successivamente aggiorna i costi delle rotte aeree
|
||||
// NOTA: il costo può variare massimo tra 0 e 100
|
||||
if(!is_valid(map, x, y) || radius<=0 || cost < -10 || cost > 10){
|
||||
fputs("KO\n", stdout);
|
||||
return false;
|
||||
}
|
||||
|
||||
int dist;
|
||||
float coeff;
|
||||
|
||||
// Le x e le y che vado a modificare sono solo quelle dentro al quadrato che inscrive la circonferenza di raggio radius
|
||||
int min_x = fmax(0, x - radius);
|
||||
int max_x = fmin(map->cols - 1, x + radius);
|
||||
int min_y = fmax(0, y - radius);
|
||||
int max_y = fmin(map->rows - 1, y + radius);
|
||||
|
||||
for(int i = min_x; i <= max_x; i++){
|
||||
for (int j = min_y; j <= max_y; j++){
|
||||
dist = hexagons_distance(x, y, i, j);
|
||||
if (dist < radius){
|
||||
coeff = fmax(0.0f, (float)(radius - dist) / (float)radius);
|
||||
int* hex_cost = &map->grid[i][j];
|
||||
*hex_cost = *hex_cost + (int)floor(cost * coeff);
|
||||
|
||||
if (*hex_cost > 100){
|
||||
*hex_cost = 100;
|
||||
}
|
||||
if (*hex_cost < 0){
|
||||
*hex_cost = 0;
|
||||
}
|
||||
|
||||
// Aggiorna le rotte aeree (NON TOCCARE)
|
||||
AirRouteNode* route_node = find_air_route_node(map->air_routes, i, j);
|
||||
if (route_node != NULL) {
|
||||
for (int counter = 0; counter < route_node->route_count; counter++){
|
||||
int cost_old_air_route = 0;
|
||||
for (int n = 0; n < counter; n++){
|
||||
cost_old_air_route += route_node->routes[n].cost;
|
||||
}
|
||||
route_node->routes[counter].cost = (int)((cost_old_air_route + *hex_cost) / (counter + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fputs("OK\n", stdout);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool toggle_air_route(HexMap* map, int x_1, int y_1, int x_2, int y_2){
|
||||
// Inserisce o rimuove una rotta aerea da un esagono
|
||||
if(!is_valid(map, x_1,y_1) || !is_valid(map, x_2,y_2)){
|
||||
fputs("KO\n", stdout);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = toggle_air_route_in_node(map->air_routes, map, x_1, y_1, x_2, y_2);
|
||||
|
||||
if (success) {
|
||||
fputs("OK\n", stdout);
|
||||
return true;
|
||||
} else {
|
||||
fputs("KO\n", stdout);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// END MAP FUNCTIONS
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN HASH TABLE FUNCTIONS
|
||||
inline int calculate_hash(int x, int y, int size){
|
||||
// Calcola l'hash dati in input le coordinate e la dimensione della hash table
|
||||
return ((x * 73 + y * 31) & (size - 1));
|
||||
}
|
||||
|
||||
inline HashTable* create_hash_table(int size){
|
||||
// Genera l'hash table con fattore di carico 1.5 per bilanciare performance temporali e spaziali. Calcola il numero primo migliore da usare come dimensione dell'hash table e poi inizializza tutti i suoi attributi
|
||||
// NOTA: con calloc() vado a inizializzare già tutti i bucket a 0, quindi non devo preoccuparmi di farli puntare a NULL
|
||||
|
||||
int hash_length=65536;
|
||||
|
||||
HashTable* ht = (HashTable*) malloc(sizeof(HashTable));
|
||||
ht->size = hash_length;
|
||||
ht->capacity = size;
|
||||
ht->buckets = calloc(hash_length, sizeof(Node*));
|
||||
ht->pool = malloc(size * sizeof(Node));
|
||||
ht->pool_index = 0;
|
||||
|
||||
return ht;
|
||||
}
|
||||
|
||||
inline void clear_hash_table(HashTable* ht){
|
||||
// Mette a 0 tutti i bucket e va a resettare il pool di memoria contigua
|
||||
// NOTA: non va a fare delle free perchè al prossimo utilizzo della hash table vado a sovrascrivere i nodi (verranno sempre scritti nel pool di memoria)
|
||||
memset(ht->buckets, 0, ht->size * sizeof(Node*));
|
||||
ht->pool_index = 0;
|
||||
}
|
||||
|
||||
void destroy_hash_table(HashTable** ht){
|
||||
// Elimina completamente una hash table (anche il suo pool di memoria contigua)
|
||||
if (*ht) {
|
||||
free((*ht)->buckets);
|
||||
free((*ht)->pool);
|
||||
free(*ht);
|
||||
*ht = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
inline Node* insert_or_update_element(HashTable* ht, int x, int y, int cost){
|
||||
// Se il nodo che sto provando ad inserire non esiste, lo inserisco in testa alla chain.
|
||||
// Se invece esiste controllo il costo: se è maggiore di quello già presente in hash table ignoro (ritorno NULL), altrimenti aggiorno il costo
|
||||
// NOTA: quando inserisco per la prima volta in hash table inizializzo heap_index a -1 per intendere che non è ancora stato inserito in heap
|
||||
int index = calculate_hash(x, y, ht->size);
|
||||
|
||||
Node* current = ht->buckets[index];
|
||||
while (current!=NULL) {
|
||||
if (current->x == x && current->y == y) {
|
||||
if (current->cost > cost) {
|
||||
current->cost = cost;
|
||||
return current;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
if (ht->pool_index >= ht->capacity){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Node* new_node = &ht->pool[ht->pool_index++];
|
||||
new_node->x = x;
|
||||
new_node->y = y;
|
||||
new_node->cost = cost;
|
||||
new_node->heap_index = -1;
|
||||
new_node->next = ht->buckets[index];
|
||||
ht->buckets[index] = new_node;
|
||||
|
||||
return new_node;
|
||||
}
|
||||
// END HASH TABLE FUNCTIONS
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN HEAP FUNCTIONS
|
||||
inline void heapify_up(MinHeap* heap, int index) {
|
||||
// Fa salire un nodo dal basso verso l'alto (lo swappo con il genitore)
|
||||
// NOTA: viene usato quando inserisco un nuovo nodo: lo inserisco come foglia e poi lo faccio risalire fino alla sua posizione corretta
|
||||
Node** queue = heap->queue;
|
||||
int parent;
|
||||
int current_cost;
|
||||
int parent_cost;
|
||||
|
||||
while (index > 0) {
|
||||
parent = (index - 1) >> 1;
|
||||
|
||||
current_cost = queue[index]->cost;
|
||||
parent_cost = queue[parent]->cost;
|
||||
|
||||
if (current_cost >= parent_cost) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Swappa i nodi
|
||||
Node* temp = queue[index];
|
||||
queue[index] = queue[parent];
|
||||
queue[parent] = temp;
|
||||
|
||||
// Aggiorna gli indici
|
||||
queue[index]->heap_index = index;
|
||||
queue[parent]->heap_index = parent;
|
||||
|
||||
index = parent;
|
||||
}
|
||||
}
|
||||
|
||||
inline void heapify_down(MinHeap* heap, int index) {
|
||||
// Fa scendere un nodo dall'alto verso il basso (lo swappo con un figlio)
|
||||
// NOTA: viene usato quando consumo il nodo minimo (root) in Dijkstra: metto il nodo più grande di tutti come root e poi lo faccio scendere fino alla posizione corretta
|
||||
int left, right, smallest;
|
||||
const int size = heap->size;
|
||||
Node** queue = heap->queue;
|
||||
int current_cost;
|
||||
|
||||
while (true) {
|
||||
left = (index << 1) + 1;
|
||||
right = left + 1;
|
||||
smallest = index;
|
||||
|
||||
current_cost = queue[smallest]->cost;
|
||||
|
||||
// Figlio sinistro
|
||||
if (left < size) {
|
||||
int left_cost = queue[left]->cost;
|
||||
if (left_cost < current_cost) {
|
||||
smallest = left;
|
||||
current_cost = left_cost;
|
||||
}
|
||||
}
|
||||
|
||||
// Figlio destro
|
||||
if (right < size) {
|
||||
int right_cost = queue[right]->cost;
|
||||
if (right_cost < current_cost) {
|
||||
smallest = right;
|
||||
}
|
||||
}
|
||||
|
||||
if (smallest == index) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Swap dei nodi
|
||||
Node* temp = queue[index];
|
||||
queue[index] = queue[smallest];
|
||||
queue[smallest] = temp;
|
||||
|
||||
// Aggiorna gli indici
|
||||
queue[index]->heap_index = index;
|
||||
queue[smallest]->heap_index = smallest;
|
||||
|
||||
index = smallest;
|
||||
}
|
||||
}
|
||||
|
||||
inline void heap_enqueue(MinHeap* heap, Node* node){
|
||||
// Se il nodo non è già presente in heap (index==-1) allora lo aggiungo come foglia e poi lo faccio risalire.
|
||||
// Se invece il nodo esiste già, gli aggiorno il costo e poi lo faccio risalire (il nuovo costo è per forza minore, quindi deve salire)
|
||||
// NOTA: sono sicuro che il costo nuovo sia inferiore del precedente perchè chiamo l'heap_enqueue solo dopo aver controllato tramite la hash table
|
||||
if (node->heap_index == -1) {
|
||||
if(heap->size >= heap->capacity){
|
||||
return;
|
||||
}
|
||||
|
||||
node->heap_index = heap->size;
|
||||
heap->queue[heap->size] = node;
|
||||
heap->size++;
|
||||
|
||||
heapify_up(heap, node->heap_index);
|
||||
} else {
|
||||
heapify_up(heap, node->heap_index);
|
||||
}
|
||||
}
|
||||
|
||||
inline Node* heap_dequeue(MinHeap* heap){
|
||||
// Consumo il primo nodo della heap: gli imposto l'index a -1 per intendere che non è più in heap e poi lo ritorno
|
||||
// NOTA: per sistemare l'heap metto come root il nodo più grande (quello all'ultimo indice) in root e poi lo faccio scendere
|
||||
if (heap->size == 0){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Node* min = heap->queue[0];
|
||||
min->heap_index = -1;
|
||||
|
||||
heap->size--;
|
||||
|
||||
if (heap->size > 0) {
|
||||
heap->queue[0] = heap->queue[heap->size];
|
||||
heap->queue[0]->heap_index = 0;
|
||||
heapify_down(heap, 0);
|
||||
}
|
||||
|
||||
return min;
|
||||
}
|
||||
|
||||
inline MinHeap* heap_create(int capacity){
|
||||
// Crea l'heap
|
||||
MinHeap* heap = malloc(sizeof(MinHeap));
|
||||
heap->queue = malloc(sizeof(Node*) * capacity);
|
||||
heap->size = 0;
|
||||
heap->capacity = capacity;
|
||||
return heap;
|
||||
}
|
||||
|
||||
inline void heap_clear(MinHeap* heap){
|
||||
// Imposta semplicemente la dimensione dell'heap a 0, tanto ai successivi usi vado a sovrascrivere i puntatori che sono presenti
|
||||
heap->size = 0;
|
||||
}
|
||||
|
||||
void heap_destroy(MinHeap* heap){
|
||||
// Vado ad eliminare completamente l'heap
|
||||
free(heap->queue);
|
||||
free(heap);
|
||||
}
|
||||
// END HEAP FUNCTIONS
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN DIJKSTRA
|
||||
int travel_cost(HexMap* map, HashTable* ht, MinHeap* heap, int xp, int yp, int xd, int yd) {
|
||||
// Inserisco il nodo sorgente in hash table e in heap.
|
||||
// Nel while (che va avanti finchè non esaurisco i nodi nell'heap) faccio:
|
||||
// - Prendo il primo elemento dall'heap (quindi il minimo)
|
||||
// - Se è il nodo destinazione, ritorno il costo di raggiungimento (non mi serve esplorare ulteriormente per come è fatto Dijkstra, appena lo trovo ho già trovato il costo minore)
|
||||
// - Controllo i 6 nodi vicini e, se il loro costo di raggiungimento è minore di quello che già hanno in hash table (oppure se vengono inseriti per la prima volta in hash table), vado ad inserirli anche in heap
|
||||
// - Controllo tutte le rotte aeree del nodo e, se il nodo di destinazione ha costo di raggiungimento minore di quello già presente in hash table, lo inserisco in heap
|
||||
// Controllo alla fine il costo di raggiungimento del nodo di destinazione (teoricamente non dovrei mai arrivarci qui)
|
||||
// Pulisco heap e hash table
|
||||
|
||||
// Controllo la validità delle coordinate
|
||||
if ((unsigned)xp >= map->cols || (unsigned)yp >= map->rows || (unsigned)xd >= map->cols || (unsigned)yd >= map->rows) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Esco se partenza e destinazione coincidono
|
||||
if (xp == xd && yp == yd){
|
||||
return 0;
|
||||
}
|
||||
|
||||
Node* new_node = insert_or_update_element(ht, xp, yp, 0);
|
||||
heap_enqueue(heap, new_node);
|
||||
|
||||
const int cols = map->cols;
|
||||
const int rows = map->rows;
|
||||
|
||||
static const int dir_even[6][2] = {
|
||||
{+1, 0}, { 0, -1}, {+1, -1},
|
||||
{-1, 0}, {+1, +1}, { 0, +1}
|
||||
};
|
||||
static const int dir_odd[6][2] = {
|
||||
{+1, 0}, {-1, -1}, { 0, -1},
|
||||
{-1, 0}, { 0, +1}, {-1, +1}
|
||||
};
|
||||
|
||||
while (heap->size > 0) {
|
||||
Node* current = heap_dequeue(heap);
|
||||
|
||||
// Se current==destinazione
|
||||
if (current->x == xd && current->y == yd) {
|
||||
int result = current->cost;
|
||||
heap_clear(heap);
|
||||
clear_hash_table(ht);
|
||||
return result;
|
||||
}
|
||||
|
||||
const int land_cost = map->grid[current->x][current->y];
|
||||
|
||||
if (land_cost == 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
// Scelgo i vicini in base alla parità
|
||||
const int (*dirs)[2] = (current->y & 1) ? dir_even : dir_odd;
|
||||
int new_cost = current->cost + land_cost;
|
||||
|
||||
// Controllo i 6 vicini
|
||||
for (int i = 0; i < 6; i++) {
|
||||
int new_x = current->x + dirs[i][0];
|
||||
int new_y = current->y + dirs[i][1];
|
||||
|
||||
if ((unsigned)new_x < cols && (unsigned)new_y < rows) {
|
||||
new_node = insert_or_update_element(ht, new_x, new_y, new_cost);
|
||||
if (new_node) {
|
||||
heap_enqueue(heap, new_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Controllo le rotte aeree
|
||||
AirRouteNode* route_node = find_air_route_node(map->air_routes, current->x, current->y);
|
||||
if (route_node != NULL) {
|
||||
for (int i = 0; i < route_node->route_count; i++) {
|
||||
new_node = insert_or_update_element(ht,route_node->routes[i].dest_x, route_node->routes[i].dest_y, current->cost + route_node->routes[i].cost);
|
||||
if (new_node) {
|
||||
heap_enqueue(heap, new_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Destinazione non raggiunta
|
||||
heap_clear(heap);
|
||||
clear_hash_table(ht);
|
||||
return -1;
|
||||
}
|
||||
// END DIJKSTRA
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN CACHE FUNCTIONS
|
||||
inline int calculate_cache_hash(int xp, int yp, int xd, int yd, int size){
|
||||
// Calcola l'hash dati in input le quattro coordinate e la dimensione della hash table della cache
|
||||
return ((xp * 19 + yp * 13 + xd * 27 + yd * 121) & (size-1));
|
||||
}
|
||||
|
||||
inline CacheHashTable* create_cache(int capacity){
|
||||
// Crea la cache con un fattore di carico di circa 1.2 (per massimizzare efficienza temporale e spaziale).
|
||||
// Inizializza tutti gli attributi della tabella hash stessa (dimensione, capacità, pool ecc...) e e anche della lista doppiamente concatenata LRU (head e tail)
|
||||
|
||||
int hash_length = CACHE_SIZE;
|
||||
|
||||
// Hash table
|
||||
CacheHashTable* cache = (CacheHashTable*)malloc(sizeof(CacheHashTable));
|
||||
cache->size = hash_length;
|
||||
cache->capacity = capacity;
|
||||
cache->element_number = 0;
|
||||
cache->buckets = calloc(hash_length, sizeof(CacheNode*));
|
||||
cache->pool = malloc(capacity * sizeof(CacheNode));
|
||||
cache->pool_index = 0;
|
||||
|
||||
// Lista LRU
|
||||
cache->head = &cache->pool[cache->pool_index++];
|
||||
cache->tail = &cache->pool[cache->pool_index++];
|
||||
cache->head->lru_next = cache->tail;
|
||||
cache->tail->lru_prev = cache->head;
|
||||
cache->head->lru_prev = NULL;
|
||||
cache->tail->lru_next = NULL;
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
inline void lru_move_to_head(CacheHashTable* cache, CacheNode* node){
|
||||
// Prende in ingresso un nodo già presente in lista e lo inserisce in testa (quando faccio una lookup deve andare in testa)
|
||||
|
||||
// Gestisce i puntatori prima di spostare il nodo
|
||||
if (node->lru_prev){
|
||||
node->lru_prev->lru_next = node->lru_next;
|
||||
}
|
||||
if (node->lru_next){
|
||||
node->lru_next->lru_prev = node->lru_prev;
|
||||
}
|
||||
|
||||
// Sposta il nodo in testa
|
||||
node->lru_next = cache->head->lru_next;
|
||||
node->lru_prev = cache->head;
|
||||
cache->head->lru_next->lru_prev = node;
|
||||
cache->head->lru_next = node;
|
||||
}
|
||||
|
||||
inline void remove_node(CacheHashTable* cache, CacheNode* node, int xp, int yp, int xd, int yd){
|
||||
// Rimuove un nodo sia dalla lista che dalla hash table
|
||||
|
||||
// Rimozione dalla hash table
|
||||
int index = calculate_cache_hash(xp, yp, xd, yd, cache->size);
|
||||
CacheNode* current = cache->buckets[index];
|
||||
CacheNode* prev = NULL;
|
||||
|
||||
while (current != NULL) {
|
||||
if (current == node) {
|
||||
if (prev == NULL) {
|
||||
cache->buckets[index] = current->next;
|
||||
} else {
|
||||
prev->next = current->next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
prev = current;
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
// Rimozione dalla lista LRU
|
||||
if (node->lru_prev) node->lru_prev->lru_next = node->lru_next;
|
||||
if (node->lru_next) node->lru_next->lru_prev = node->lru_prev;
|
||||
|
||||
cache->element_number--;
|
||||
}
|
||||
|
||||
inline CacheNode* cache_lookup(CacheHashTable* cache, int xp, int yp, int xd, int yd){
|
||||
// Cerca un nodo in cache e, se lo trova, lo muove in testa nella lista LRU
|
||||
int index = calculate_cache_hash(xp, yp, xd, yd, cache->size);
|
||||
CacheNode* current = cache->buckets[index];
|
||||
|
||||
while (current != NULL) {
|
||||
if (current->xp == xp && current->yp == yp && current->xd == xd && current->yd == yd) { // Se viene trovato il nodo
|
||||
lru_move_to_head(cache, current);
|
||||
return current;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
return NULL; // Se non viene trovato, restituisco NULL
|
||||
}
|
||||
|
||||
inline CacheNode* cache_insert(CacheHashTable* cache, int xp, int yp, int xd, int yd, int cost){
|
||||
// Cerca nell'hash table se il nodo esiste già (nella lookup c'è già lo spostamento in testa). Nel caso aggiorna il costo
|
||||
CacheNode* existing = cache_lookup(cache, xp, yp, xd, yd);
|
||||
if (existing != NULL) {
|
||||
existing->cost = cost;
|
||||
return existing;
|
||||
}
|
||||
|
||||
// Se la cache è piena, rimuovo il nodo in coda
|
||||
if (cache->element_number >= cache->capacity) {
|
||||
CacheNode* lru_node = cache->tail->lru_prev;
|
||||
if (lru_node != cache->head) {
|
||||
remove_node(cache, lru_node, lru_node->xp, lru_node->yp, lru_node->xd, lru_node->yd);
|
||||
}
|
||||
}
|
||||
|
||||
// Arrivo qua solamente se il nodo non è già presente in cache; creo il nodo
|
||||
CacheNode* new_node = &cache->pool[cache->pool_index++];
|
||||
new_node->xp = xp;
|
||||
new_node->yp = yp;
|
||||
new_node->xd = xd;
|
||||
new_node->yd = yd;
|
||||
new_node->cost = cost;
|
||||
|
||||
// Lo aggiungo in hash table
|
||||
int index = calculate_cache_hash(xp, yp, xd, yd, cache->size);
|
||||
new_node->next = cache->buckets[index];
|
||||
cache->buckets[index] = new_node;
|
||||
|
||||
// Lo aggiungo in testa alla coda
|
||||
new_node->lru_next = cache->head->lru_next;
|
||||
new_node->lru_prev = cache->head;
|
||||
cache->head->lru_next->lru_prev = new_node;
|
||||
cache->head->lru_next = new_node;
|
||||
|
||||
cache->element_number++;
|
||||
return new_node;
|
||||
}
|
||||
|
||||
inline void cache_remove(CacheHashTable* cache, int xp, int yp, int xd, int yd){
|
||||
// Trova un nodo in cache e lo rimuove sia dalla hash table che dalla lista LRU
|
||||
int index = calculate_cache_hash(xp, yp, xd, yd, cache->size);
|
||||
CacheNode* current = cache->buckets[index];
|
||||
|
||||
while (current != NULL) {
|
||||
if (current->xp == xp && current->yp == yp && current->xd == xd && current->yd == yd) {
|
||||
remove_node(cache, current, xp, yp, xd, yd); // Rimuove sia dalla hash table che dalla lista LRU
|
||||
return;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
|
||||
void clear_cache(CacheHashTable* cache){
|
||||
// Pulisce tutta la cache ma ne mantiene la struttura (per non dover reinizializzare ogni volta la hash table)
|
||||
// NOTA: non faccio la free sugli elementi perchè sono tutti nella pool: verranno sovrascritti dopo
|
||||
memset(cache->buckets, 0, cache->size * sizeof(CacheNode*));
|
||||
cache->pool_index = 0;
|
||||
cache->element_number = 0;
|
||||
|
||||
// Resetta head e tail
|
||||
cache->head = &cache->pool[cache->pool_index++];
|
||||
cache->tail = &cache->pool[cache->pool_index++];
|
||||
cache->head->lru_next = cache->tail;
|
||||
cache->tail->lru_prev = cache->head;
|
||||
cache->head->lru_prev = NULL;
|
||||
cache->tail->lru_next = NULL;
|
||||
}
|
||||
|
||||
void destroy_cache(CacheHashTable** cache){
|
||||
// Elimino completamente la struttura e ripulisco la pool
|
||||
if (*cache) {
|
||||
free((*cache)->buckets);
|
||||
free((*cache)->pool);
|
||||
free(*cache);
|
||||
*cache = NULL;
|
||||
}
|
||||
}
|
||||
// END CACHE FUNCTIONS
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// BEGIN MAIN
|
||||
int main(){
|
||||
char testo[17];
|
||||
int inp_uno, inp_due, inp_tre, inp_quattro;
|
||||
|
||||
HexMap map;
|
||||
HashTable* ht = NULL;
|
||||
MinHeap* heap = NULL;
|
||||
CacheHashTable* cache = NULL;
|
||||
|
||||
int already_initialized=0;
|
||||
CacheNode* cached;
|
||||
int cost;
|
||||
|
||||
while (true){
|
||||
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 (heap!=NULL){
|
||||
heap_destroy(heap);
|
||||
}
|
||||
heap = heap_create(inp_uno * inp_due);
|
||||
|
||||
if (cache!=NULL){
|
||||
destroy_cache(&cache);
|
||||
}
|
||||
cache = create_cache(CACHE_SIZE);
|
||||
}
|
||||
|
||||
if (strcmp(testo, "print")==0){
|
||||
if (already_initialized==0){
|
||||
fprintf(stdout, "-1\n");
|
||||
continue;
|
||||
}
|
||||
print_map(&map);
|
||||
}
|
||||
|
||||
if (strcmp(testo, "change_cost")==0){
|
||||
if (already_initialized==0){
|
||||
fprintf(stdout, "-1\n");
|
||||
continue;
|
||||
}
|
||||
if(change_cost(&map, inp_uno, inp_due, inp_tre, inp_quattro)){
|
||||
clear_cache(cache);
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(testo, "toggle_air_route")==0){
|
||||
if (already_initialized==0){
|
||||
fprintf(stdout, "-1\n");
|
||||
continue;
|
||||
}
|
||||
if(toggle_air_route(&map, inp_uno, inp_due, inp_tre, inp_quattro)){
|
||||
clear_cache(cache);
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(testo, "travel_cost")==0){
|
||||
if (already_initialized==0){
|
||||
fprintf(stdout, "-1\n");
|
||||
continue;
|
||||
}
|
||||
cached=cache_lookup(cache, inp_uno, inp_due, inp_tre, inp_quattro);
|
||||
|
||||
if(cached!=NULL){
|
||||
cost = cached->cost;
|
||||
} else {
|
||||
cost = travel_cost(&map, ht, heap, inp_uno, inp_due, inp_tre, inp_quattro);
|
||||
cache_insert(cache, inp_uno, inp_due, inp_tre, inp_quattro, cost);
|
||||
}
|
||||
|
||||
fprintf(stdout, "%d\n", cost);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
if (already_initialized==1) {
|
||||
destroy_map(&map);
|
||||
}
|
||||
if (ht!=NULL) {
|
||||
destroy_hash_table(&ht);
|
||||
}
|
||||
if (heap!=NULL) {
|
||||
heap_destroy(heap);
|
||||
}
|
||||
if (cache!=NULL) {
|
||||
destroy_cache(&cache);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
// END
|
||||
+60410
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user