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;