#define DISKS_NUM           6
#define PEDESTAL_HEIGHT     20
#define DISK_HEIGHT         7
#define DISK_UNIT_WIDTH     5

I64 poles_x[3];
I64 disks_x[DISKS_NUM], disks_y[DISKS_NUM], disks_pole[DISKS_NUM];

I64 OtherPole(I64 pole1, I64 pole2)
{
    return 3 - pole1 - pole2;
}
 
I64 TopDisk(I64 pole)
{
    I64 i;

    for (i = 0; i < DISKS_NUM; i++)
        if (disks_pole[i] == pole)
            return i;

    return -1;
}

I64 PosInStack(I64 pole, I64 disk)
{
    I64 res = 0, i;

    for (i = DISKS_NUM - 1; i > disk; i--)
        if (disks_pole[i] == pole)
            res++;

    return res;
}

U0 SetDisksRestXY()
{
    I64 i;

    for (i = 0; i < DISKS_NUM; i++)
    {
        disks_x[i] = poles_x[disks_pole[i]];
        disks_y[i] = Fs->pix_height -
                        PEDESTAL_HEIGHT - (DISK_HEIGHT + 1) / 2 - 1 - (DISK_HEIGHT + 1) * PosInStack(disks_pole[i], i);
    }
}

U0 DrawIt(CTask *task, CDC *dc)
{
    I64 i;

    for (i = 0; i < 3; i++)
        poles_x[i] = (1 + i) * task->pix_width / 4;

    dc->color = BLACK;
    GrRect(dc,  poles_x[0] - 50, task->pix_height - PEDESTAL_HEIGHT,
                poles_x[2] - poles_x[0] + 100, PEDESTAL_HEIGHT - FONT_HEIGHT);
    dc->color = DKGRAY;
    GrRect(dc,  poles_x[0] - 49, task->pix_height - PEDESTAL_HEIGHT + 1, 
                poles_x[2] - poles_x[0] + 98, PEDESTAL_HEIGHT - FONT_HEIGHT - 2);

    for (i = 0; i < 3; i++)
    {
        dc->color = BLACK;
        GrRect(dc,  poles_x[i] - 3, 
                    task->pix_height - PEDESTAL_HEIGHT - (DISKS_NUM + 1) * (DISK_HEIGHT + 1),
                    7, (DISKS_NUM + 1) * (DISK_HEIGHT + 1));
        dc->color = YELLOW;
        GrRect(dc,  poles_x[i] - 2, 
                    task->pix_height - PEDESTAL_HEIGHT + 1 - (DISKS_NUM + 1) * (DISK_HEIGHT + 1), 
                    5, (DISKS_NUM + 1) * (DISK_HEIGHT + 1) - 1);
    }

    for (i = 0; i < DISKS_NUM; i++)
    {
        dc->color = BLACK;
        GrRect(dc,  disks_x[i] - (i + 1) * DISK_UNIT_WIDTH, 
                    disks_y[i] - DISK_HEIGHT / 2, (i + 1) * (DISK_UNIT_WIDTH * 2) + 1, DISK_HEIGHT);
        dc->color = gr_rainbow_10[i];
        GrRect(dc,  disks_x[i] - (i + 1) * DISK_UNIT_WIDTH + 1, 
                    disks_y[i] - DISK_HEIGHT / 2 + 1, (i + 1) * (DISK_UNIT_WIDTH * 2) - 1, DISK_HEIGHT - 2);
    }
}

U0 MySleep()
{
    if (CharScan)
        throw;
    Sleep(3);
}

U0 MoveDisks(I64 src_pole, I64 dst_pole, I64 num)
{
    I64 top, x, y;

    if (num > 1)
        MoveDisks(src_pole, OtherPole(src_pole, dst_pole), num - 1);
    DocClear;
    "$CM+BY,0,0$Disk:%d from %d to %d\n", TopDisk(src_pole), src_pole, dst_pole;

    top = TopDisk(src_pole);
    for (y = disks_y[top];
         y > Fs->pix_height - PEDESTAL_HEIGHT - (DISK_HEIGHT + 1) / 2 - (DISK_HEIGHT + 1) * (DISKS_NUM + 2);
         y--)
    {
        disks_y[top] = y;
        MySleep;
    }
    if (src_pole < dst_pole)
        for (x = poles_x[src_pole]; x <= poles_x[dst_pole]; x++)
        {
            disks_x[top] = x;
            MySleep;
        }
    else
        for (x = poles_x[src_pole]; x >= poles_x[dst_pole]; x--)
        {
            disks_x[top] = x;
            MySleep;
        }

    disks_pole[top] = dst_pole;
    for (y = disks_y[top];
         y < Fs->pix_height - PEDESTAL_HEIGHT - (DISK_HEIGHT + 1) / 2 - 1 - (DISK_HEIGHT + 1) * PosInStack(dst_pole, top);
         y++)
    {
        disks_y[top] = y;
        MySleep;
    }
    SetDisksRestXY;
    if (num > 1)
        MoveDisks(OtherPole(src_pole, dst_pole), dst_pole, num - 1);
}

U0 Init()
{
    I64 i;

    for (i = 0; i < 3; i++)
        poles_x[i] = (1 + i) * Fs->pix_width / 4;

    for (i = 0; i < DISKS_NUM; i++)
        disks_pole[i] = 0;

    SetDisksRestXY;
}

U0 Hanoi()
{
    SettingsPush; //See SettingsPush
    Init;
    DocClear;
    Fs->draw_it = &DrawIt;

    Sleep(1000);
    try
    {
        MoveDisks(0, 2, DISKS_NUM);
        Beep; Beep;
        DocClear;
        DocBottom;
        PressAKey;
    }
    catch
        PutExcept;
    DocClear;
    SettingsPop;
}

Hanoi;