Point moveSheep(int sheepId) {
        Point thisSheep = sheeps[sheepId];
        double dspeed = 0;
        Point closestDog = getClosestDog(sheepId);
        double dist = distance(thisSheep, closestDog);
        assert dist > 0;

        if (dist < RUN_DIST)
            dspeed = RUN_SPEED;
        else if (dist < WALK_DIST)
            dspeed = WALK_SPEED;

        // offset from dogs
        double ox_dog = (thisSheep.x - closestDog.x) / dist * dspeed;
        double oy_dog = (thisSheep.y - closestDog.y) / dist * dspeed;

        // offset from clustering
        double ox_cluster = 0, oy_cluster = 0;

        // aggregate offsets then normalize
        for (int i = 0; i < nsheeps; ++i) {
            // skip this sheep itself
            if (i == sheepId) continue;

            double d = distance(thisSheep, sheeps[i]);

            // ignore overlapping sheep
            if (d < CLUSTER_DIST && d > 0) {
                // add an unit vector to x-axis, y-axis
                ox_cluster += ((thisSheep.x - sheeps[i].x) / d);
                oy_cluster += ((thisSheep.y - sheeps[i].y) / d);
            }
        }
        // normalize by length
        double l = vectorLength(ox_cluster, oy_cluster);
        if (l != 0) {
            ox_cluster = ox_cluster / l * CLUSTER_SPEED;
            oy_cluster = oy_cluster / l * CLUSTER_SPEED;
        }

        double ox = ox_dog + ox_cluster, oy = oy_dog + oy_cluster;

        Point npos = updatePosition(thisSheep, ox, oy);

        if (verbose) {
            if (!npos.equals(thisSheep)) {
                //System.err.format("Sheep %d moves from (%.2f,%.2f) to (%.2f,%.2f): %.2f\n", sheepId, thisSheep.x, thisSheep.y, npos.x, npos.y, distance(thisSheep, npos));
            }
            else {
                //System.err.format("Sheep %d stays at (%.2f,%.2f)\n", sheepId, thisSheep.x, thisSheep.y);
            }
        }
        return npos;

    }