U0 InitDefines()
{
    I64 w = GR_WIDTH, h = GR_HEIGHT - FONT_HEIGHT, cols, rows, size = PopUpRangeI64(1,9,, "Size\n"), rad = 16 - size;

    if (size < 0)
        throw;
    DefinePrint("UNITS_NUM", "%d", 4096 / SqrI64(rad) & ~1);
    DefinePrint("CIRCLE_RAD", "%d", rad);
    DefinePrint("MOVE_CIRCLES", "%d", 65 / rad);

    cols = (w - 8 - rad) / (2 * rad) & ~1 - 1;
    DefinePrint("BORDER_X", "%d", (w + rad - cols * 2 * rad) / 2);

    rows = (h - 8) / (2 * rad) & ~1;
    DefinePrint("BORDER_Y","%d", (h - rows * 2 * rad) / 2);

} InitDefines;

class Unit
{
    I64  num, x, y, player, link, color;
    Bool alive, king;

} u[UNITS_NUM];

CDC *map_dc;
I64  cur_player, num_alive[2], start_x, start_y, end_x, end_y;
Bool show_start;

U0 S2Circle(I64 *_x, I64 *_y)
{
    I64 i, j;

    j = (*_y - BORDER_Y) / (CIRCLE_RAD * 2);
    if (j & 1)
        i = (*_x - CIRCLE_RAD - BORDER_X) / (CIRCLE_RAD * 2);
    else
        i = (*_x - BORDER_X) / (CIRCLE_RAD * 2);
    *_y = j;
    *_x = i;
}

U0 Circle2S(I64 *_x, I64 *_y)
{
    I64 j = *_y, i = *_x;

    *_y = j * CIRCLE_RAD * 2 + CIRCLE_RAD + BORDER_Y;
    *_x = i * CIRCLE_RAD * 2 + CIRCLE_RAD + BORDER_X;
    if (j & 1)
        *_x += CIRCLE_RAD;
}

U0 S2W(I64 *_x, I64 *_y)
{
    S2Circle(_x, _y);
    Circle2S(_x, _y);
}

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

    map_dc->flags |= DCF_NO_TRANSPARENTS;
    GrBlot(dc, 0, 0, map_dc);

    if (cur_player == 0)
    {
        dc->color = LTCYAN;
        GrFloodFill(dc, 0, 0);
        dc->color = CYAN;
    }
    else
    {
        dc->color = LTPURPLE;
        GrFloodFill(dc, 0, 0);
        dc->color = PURPLE;
    }
    GrPrint(dc, 2, 2, "Player %d", cur_player + 1);

    for (i = 0; i < UNITS_NUM; i++)
        if (u[i].alive)
        {
            dc->color = u[i].color;
            GrFloodFill(dc, u[i].x, u[i].y);
        }
    for (i = 0; i < UNITS_NUM; i++)
        if (u[i].alive && !u[i].king)
        {
            dc->color = BLACK;
            GrLine(dc, u[i].x, u[i].y, u[u[i].link].x, u[u[i].link].y);
            if (Blink)
            {
                dc->color = BLUE;
                dc->thick = 6;
                GrPlot3(dc, (u[i].x + u[u[i].link].x) >> 1, (u[i].y + u[u[i].link].y) >> 1, 0);
            }
        }
    if (show_start)
    {
        dc->color = LTRED;
        GrLine(dc, start_x - 4, start_y - 4, start_x + 4, start_y + 4);
        GrLine(dc, start_x - 4, start_y + 4, start_x + 4, start_y - 4);
        GrLine(dc, start_x, start_y, end_x, end_y);
    }
    if ((num_alive[0] == 1 || num_alive[1] == 1) && Blink(4))
    {
        dc->color = BLACK;
        GrPrint(dc, (task->pix_width  - FONT_WIDTH * 9) >> 1, (task->pix_height - FONT_HEIGHT) >> 1, "Game Over");
    }
}

Unit *UnitFind(I64 x, I64 y, I64 player)
{
    I64   i, dd, best_dd = I64_MAX;
    Unit *res = NULL;

    for (i = 0; i < UNITS_NUM; i++)
    {
        if (u[i].player == player && u[i].alive)
        {
            dd = SqrI64(u[i].x - x) + SqrI64(u[i].y - y);
            if (dd < best_dd)
            {
                best_dd = dd;
                res = &u[i];
            }
        }
    }

    return res;
}

U0 KillsCheck(I64 x1, I64 y1, I64 player)
{
    I64  i, j, x2, y2, dd;
    Bool found;

    for (i = 0; i < UNITS_NUM; i++)
    {
        if (u[i].player != player && u[i].alive && !u[i].king)
        {
            x2 = (u[i].x + u[u[i].link].x) >> 1;
            y2 = (u[i].y + u[u[i].link].y) >> 1;
            dd = SqrI64(x2 - x1) + SqrI64(y2 - y1);
            if (dd <= (CIRCLE_RAD + 2) * (CIRCLE_RAD + 2))
            {
                u[i].alive = FALSE;
                Sound(62);
                Sleep(100);
                Sound;
                Sleep(25);
                num_alive[u[i].player]--;
                do
                {
                    found = FALSE;
                    for (j = 0; j < UNITS_NUM; j++)
                        if (u[j].alive && u[j].player != player && !u[j].king && !u[u[j].link].alive)
                        {
                            found = TRUE;
                            u[j].alive = FALSE;
                            Sound(62);
                            Sleep(100);
                            Sound;
                            Sleep(25);
                            num_alive[u[j].player]--;
                        }
                }
                while (found);
            }
        }
    }
}

Bool UnitMove(Unit *tmpu, I64 x2, I64 y2)
{
    I64 i, r, c, r2, c2, x = start_x, y = start_y;

    S2W(&x2, &y2);
    c2 = x2;
    r2 = y2;
    S2Circle(&c2, &r2);
    for (i = 0; i < MOVE_CIRCLES + 1; i++)
    {
        c = x;
        r = y;
        S2Circle(&c, &r);
        if (c == c2 && r == r2)
        {
            end_x = tmpu->x = x2;
            end_y = tmpu->y = y2;
            return TRUE;
        }
        if (r2 != r)
        {
            if (r&1)
            {
                if (c < c2)
                    c++;
            }
            else
            {
                if (c > c2)
                    c--;
            }
        }
        if (r2 > r)
        {
            x = c;
            y = ++r;
            Circle2S(&x, &y);
        }
        else if (r2 < r)
        {
            x = c;
            y = --r;
            Circle2S(&x, &y);
        }
        else if (c2 > c)
        {
            x = ++c;
            y = r;
            Circle2S(&x, &y);
        }
        else if (c2 < c)
        {
            x = --c;
            y = r;
            Circle2S(&x, &y);
        }
    }

    return FALSE;
}

CDC *DrawHexMap(I64 *_w, I64 *_h)
{
    CDC *dc = DCNew(*_w, *_h);
    I64  i, j, x, y;

    *_w = (dc->width  - BORDER_X * 2 - CIRCLE_RAD)  / (CIRCLE_RAD * 2);
    *_h = (dc->height - BORDER_Y * 2)               / (CIRCLE_RAD * 2);
    DCFill(dc, WHITE);
    dc->color =LTGRAY;
    for (j = 0; j < *_h; j++)
    {
        for (i = 0; i < *_w; i++)
        {
            x = i * CIRCLE_RAD * 2 + BORDER_X + CIRCLE_RAD;
            y = j * CIRCLE_RAD * 2 + BORDER_Y + CIRCLE_RAD;
            if (j & 1)
                x += CIRCLE_RAD;
            S2W(&x, &y);
            GrCircle(dc, x, y, CIRCLE_RAD);
        }
    }
    *_w *= CIRCLE_RAD * 2;
    *_h *= CIRCLE_RAD * 2;

    return dc;
}

U0 Init()
{
    I64 i, j, j1, j2, dd, best_dd, best, w = Fs->pix_width, h = Fs->pix_height;

    map_dc = DrawHexMap(&w, &h);
    cur_player = 0;

    for (i = 0; i < UNITS_NUM / 2; i++)
    {
        u[i].num = i;
        u[i + UNITS_NUM / 2].num = i + UNITS_NUM / 2;

ti_restart:
        u[i].y = FloorI64(RandU32 % h, CIRCLE_RAD * 2) + CIRCLE_RAD;
        u[i + UNITS_NUM / 2].y = h - 1 - u[i].y;
        j1 = u[i].y / (CIRCLE_RAD * 2);
        j2 = u[i + UNITS_NUM / 2].y / (CIRCLE_RAD * 2);
        u[i].y += BORDER_Y;
        u[i + UNITS_NUM / 2].y += BORDER_Y;

        if (!i)
        {
            u[i].x = CIRCLE_RAD;
            if (j1 & 1)
                goto ti_restart;
        }
        else
            u[i].x = FloorI64(RandU32 % ((w - CIRCLE_RAD * 2 * 2) / 2), CIRCLE_RAD * 2) + CIRCLE_RAD;
        u[i + UNITS_NUM / 2].x = w - 1 - u[i].x;

        if (j1 & 1)
            u[i].x += CIRCLE_RAD;
        if (j2 & 1)
            u[i + UNITS_NUM / 2].x += CIRCLE_RAD;
        u[i].x += BORDER_X;
        u[i + UNITS_NUM / 2].x += BORDER_X;

        S2W(&u[i].x, &u[i].y);
        S2W(&u[i + UNITS_NUM / 2].x, &u[i + UNITS_NUM / 2].y);

        u[i].player = 0;
        u[i + UNITS_NUM / 2].player = 1;

        u[i].alive = TRUE;
        u[i + UNITS_NUM / 2].alive = TRUE;

        if (!i)
        {
            u[i].color = LTCYAN;
            u[i + UNITS_NUM / 2].color = LTPURPLE;
            u[i].king = TRUE;
            u[i + UNITS_NUM / 2].king = TRUE;
        }
        else
        {
            u[i].color = CYAN;
            u[i + UNITS_NUM / 2].color = PURPLE;
            u[i].king = FALSE;
            u[i + UNITS_NUM / 2].king = FALSE;
        }
    }
    for (i = 0; i < UNITS_NUM / 2; i++)
    {
        if (!u[i].king)
        {
            best_dd = I64_MAX;
            for (j = 0; j < UNITS_NUM / 2; j++)
            {
                if (i != j)
                {
                    dd = SqrI64(u[i].x - u[j].x) + SqrI64(u[i].y - u[j].y);
                    if ((u[j].x < u[i].x || u[j].king) && dd < best_dd )
                    {
                        best_dd = dd;
                        best = j;
                    }
                }
            }
            u[i].link = best;
            u[i + UNITS_NUM / 2].link = best + UNITS_NUM / 2;
        }
    }
    num_alive[0] = UNITS_NUM / 2;
    num_alive[1] = UNITS_NUM / 2;
}

U0 CleanUp()
{
    DCDel(map_dc);
}

U0 TreeCheckers()
{
    I64   message_code, arg1, arg2, ch, sc;
    Unit *tmpu;

    SettingsPush; //See SettingsPush
    MenuPush(   "File {"
                "  Abort(,CH_SHIFT_ESC);"
                "  Exit(,CH_ESC);"
                "}"
                "Play {"
                "  Restart(,'\n');"
                "}"
                );
    AutoComplete;
    WinBorder;
    WinMax;
    DocCursor;
    DocClear;
    PopUpOk("Step on the link mid points.\n");
    Fs->win_inhibit = WIG_TASK_DEFAULT - WIF_SELF_FOCUS - WIF_SELF_BORDER - WIF_FOCUS_TASK_MENU;
    try
    {
cn_start:
        Init;
        tmpu = NULL;
        show_start = FALSE;
        Fs->draw_it = &DrawIt;
        while (TRUE)
        {
            message_code = MessageGet(&arg1, &arg2, 
                                1 << MESSAGE_KEY_DOWN + 1 << MESSAGE_MS_L_DOWN + 1 << MESSAGE_MS_L_UP + 1 << MESSAGE_MS_MOVE);
            switch (message_code)
            {
                case MESSAGE_KEY_DOWN:
                    switch (arg1)
                    {
                        case '\n':
                            CleanUp;
                            goto cn_start;

                        case CH_ESC:
                        case CH_SHIFT_ESC:
                            goto cn_done;
                    }
                    break;

                case MESSAGE_MS_L_DOWN:
                    if (num_alive[0] > 1 && num_alive[1] > 1)
                    {
                        tmpu = UnitFind(arg1, arg2, cur_player);
                        end_x = start_x = tmpu->x;
                        end_y = start_y = tmpu->y;
                        show_start = TRUE;
                    }
                    break;

                case MESSAGE_MS_MOVE:
                    if (tmpu)
                        UnitMove(tmpu, arg1, arg2);
                    break;

                case MESSAGE_MS_L_UP:
                    UnitMove(tmpu, arg1, arg2);
                    KillsCheck(tmpu->x, tmpu->y, cur_player);
                    show_start = FALSE;
                    tmpu = NULL;
                    cur_player = 1 - cur_player;
                    break;
            }
        }
cn_done:
        MessageGet(,, 1 << MESSAGE_KEY_UP);
    }
    catch
        PutExcept;
    SettingsPop;
    CleanUp;
    MenuPop;
}

TreeCheckers;