#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <math.h>
#include <time.h>
unsigned int n; // liczba krazkow
unsigned int rods; // liczba drazkow
unsigned int moves; // liczba przelozen
/**
* Zmienne liczace czas
*/
clock_t start;
clock_t end;
/**
* Jeden element - jeden drazek
*
* Tablica *disks zawiera krazki znajdujace sie aktualnie na drazku wg. wzoru:
*
* {1, 2, 3, 4} - drazek zawiera 4 dane krazki
* {0, 0, 0, 0, 0} - drazek nie zawiera krazkow
* {1, 3, 5, 0, 0} - drazek zawiera krazki 1, 3 i 5
*/
typedef struct rod {
int num;
int *disks;
rod *next;
rod *prev;
} rod;
void init(rod**, rod**);
void add_rod(rod**, rod**);
void hanoi(rod**, rod**);
void unshift(int, int**);
void pop(int**);
void wyswietl_ulozenie(rod*);
void wyswietl_statystyki();
void time_start();
void time_stop();
double execution_time();
int main()
{
rod *head = NULL, *tail = NULL;
printf("Podaj liczbe krazkow: ");
scanf("%u", &n);
printf("Podaj liczbe drazkow: ");
scanf("%u", &rods);
if(rods == 0)
{
printf("Zbyt malo drazkow!");
exit(EXIT_FAILURE);
}
init(&head, &tail);
printf("\nInput:\n\n");
wyswietl_ulozenie(head);
// Przekladaj krazki
hanoi(&head, &tail);
printf("\nOutput:\n\n");
wyswietl_ulozenie(head);
wyswietl_statystyki();
fflush(stdin);
getchar();
return 0;
}
/**
* Inicjuj dzialanie programu - kaz utworzyc drazki
*
* @param **head
* @param **tail
*/
void init(rod **head, rod **tail)
{
for(int i = 0; i < (int)rods; i++)
{
add_rod(head, tail);
}
}
/**
* Dodaj nowy element listy bedacy drazkiem
*
* @param **head
* @param **tail
*/
void add_rod(rod **head, rod **tail)
{
rod *temp;
int count = 1;
if(*head == NULL)
{
*head = (rod*) malloc(sizeof(rod));
(*head)->disks = (int*) malloc(sizeof(int)*(n - 1));
for(int i = 0; i < (int)n; i++)
{
(*head)->disks[i] = i + 1;
}
(*head)->num = count;
(*head)->next = (*head)->prev = NULL;
*tail = *head;
}
else
{
temp = *head;
while(temp->next != NULL)
{
count++;
temp = temp->next;
}
temp->next = (rod*) malloc(sizeof(rod));
temp->next->prev = temp;
temp = temp->next;
temp->num = count + 1;
temp->disks = (int*) malloc(sizeof(int)*(n - 1));
for(int i = 0; i < n; i++)
{
temp->disks[i] = 0;
}
temp->next = NULL;
if(temp->num <= 3) // divide and conquere w przypadku n > 3 drazkow
{
*tail = temp;
}
}
}
/**
* Algorytm przekladajacy krazki wg. nastepujacego sposobu:
*
* 1. Jesli ilosc krazkow jest parzysta, przesuj najmniejszy element w prawo,
* jestli nieparzysta przesuj w lewo
* 2. Wykonaj pierwszy mozliwy ruch bez wykorzystywania najmniejszego krazka
* 3. Powtarzaj punkty 1 i 2 az do uzyskania poprawnego wyniku
*
* Więcej drążków to już kwestia powtarzania algorytmu rods - 2 razy (rods - liczba drążków),
* w tym celu *head i *tail przesuwane są co 1 pole w prawo przy każdym powtórzeniu.
*
* @param **head
* @param **tail
*/
void hanoi(rod **head, rod **tail)
{
// Zaczynamy liczyc czas
time_start();
rod *temp, *temp2, *head_t = *head;
int flag = 0, div = 0;
for(int i = 0; i < rods - 2; i++)
{
while((*tail)->disks[n - 1] != n)
{
temp = *head, flag = 0;
if(div % 2 == 0) // move smallest disk to the right if odd, or left if even
{
while(temp != (*tail)->next)
{
if(flag == 1)
{
unshift(1, &temp->disks);
break;
}
if(temp->disks[0] == 1)
{
pop(&temp->disks);
flag = 1;
}
if(n % 2 == 0)
{
temp = (temp->next == (*tail)->next) ? *head : temp->next;
}
else
{
temp = (temp->prev == (*head)->prev) ? *tail : temp->prev;
}
}
}
else // make first possible move
{
while(temp != (*tail)->next && flag == 0)
{
temp2 = *head;
while(temp2 != (*tail)->next)
{
if((temp->disks[0] < temp2->disks[0] || temp2->disks[0] == 0) && temp->disks[0] != 1 && temp->disks[0] != 0 && temp->num != temp2->num)
{
int t = temp->disks[0];
pop(&temp->disks);
unshift(t, &temp2->disks);
flag = 1;
break;
}
temp2 = temp2->next;
}
temp = temp->next;
}
}
div++, moves++;
}
// divide and conquer
if(i < rods - 3)
{
if((*head)->num <= i + 1)
*head = (*head)->next;
if((*tail)->num <= i + 3)
*tail = (*tail)->next;
}
}
*head = head_t;
// Algorytm zakonczony
time_stop();
}
/**
* Powieksza tablice o jeden element - dodaje go na poczatek tablicy, a
* wszystkie poprzednie elementy przesuwa o 1 indeks w prawo
*
* @param el Element do wstawienia na poczatek tablicy
* @param **arr
*/
void unshift(int el, int **arr)
{
for(int i = (int)n - 1; i > 0; i--)
{
(*arr)[i] = (*arr)[i - 1];
}
(*arr)[0] = el;
}
/**
* Usuwa pierwszy element z tablicy i przesywa jej pozostale elementy
* o jeden indeks w lewo
*
* @param **arr
*/
void pop(int **arr)
{
for(int i = 0; i < (int)n - 1; i++)
{
(*arr)[i] = (*arr)[i + 1];
}
(*arr)[n - 1] = 0;
}
/**
* Wyswietla ulozenie krazkow formatowane jako macierz
*
* @param *head
*/
void wyswietl_ulozenie(rod *head)
{
rod *temp = head;
for(int i = 0; i < (int)rods; i++)
{
printf("Drazek %-2d: ", i + 1);
for(int j = 0; j < (int)n; j++)
{
printf("%-3d", temp->disks[j]);
}
printf("\n");
temp = temp->next;
}
}
/**
* Wyswietla statystyki wykonywanego algorytmu
*/
void wyswietl_statystyki()
{
printf("\n\nIlosc wykonanych przelozen: %u", moves);
if(execution_time() == (double)0)
{
printf("\nCzas wykonania algorytmu jest bliski 0ms");
}
else
{
printf("\nCzas wykonania algorytmu: %lfms", execution_time());
}
}
/**
* Start counting time
*/
void time_start()
{
start = clock();
}
/**
* Finish counting
*/
void time_stop()
{
end = clock();
}
/**
* Return time passed
*/
double execution_time()
{
return (double)end - start;
}