심플 레이스 KoTH

Aug 25 2020

이 도전은 공식적으로 끝났습니다. 추가 제출은 경쟁력이 없습니다 (하지만 여전히 환영합니다!). 여기 에서 점수보기

이 챌린지에서 제출물 ( "봇")은 가능한 한 많은 레이스에서 우승하려는 자바 스크립트 함수 여야합니다. 각 레이스에서 봇은 100 유닛의 거리를 가능한 한 빨리 이동하는 데 사용해야하는 제한된 양의 에너지를 갖게됩니다.

역학

각 게임은 여러 차례의 턴으로 구성된 여러 종족으로 구성됩니다. 매 차례마다 봇은 앞으로 이동할 음이 아닌 거리를 선택합니다. 소비 된 에너지의 양은 이동 한 거리의 제곱과 같습니다 (봇은 더 먼 거리를 반환 할 경우 에너지 수준이 허용하는 한 멀리 만 이동합니다).

각 레이스가 시작될 때 봇의 위치는 0으로 재설정됩니다. 하나 이상의 봇이 100 유닛 거리에 도달하면 레이스가 종료됩니다. 봇이 처음부터 100 유닛 이상 떨어진 거리를 반환하면 이기기 위해 필요한만큼만 이동합니다. 모든 레이스가 시작될 때 봇은 마지막 레이스에서 남은 에너지 외에 100 에너지를받습니다. 그들은 또한 이전 레이스에서 여행 한 모든 유닛에 대해 1 에너지의 보너스를받습니다.

포인트들

각 레이스가 끝날 때 모든 봇은 게임이 종료 될 때 이동 한 유닛 10 개당 1 포인트를받습니다 (포인트의 일부 포함). 게임이 끝나면 가장 많은 점수를 얻은 봇이 승리합니다.

봇이 특정 수의 레이스에 최적화 될 가능성이 높기 때문에 모든 봇이 경쟁하는 5 가지 범주가 있습니다 : 250 개 레이스, 750 개 레이스, 2500 개 레이스, 7500 개 레이스, 25000 개 레이스. 봇의 전체 점수는 각 카테고리의 레이스 당 평균 점수의 합계이며 가능한 최고 점수는 50 점입니다.

입출력

봇은 다음과 같은 인수를받습니다 : dist(현재 레이스에서 이동 한 거리), energy(보유한 에너지의 양), bots(마지막 턴이 끝날 때까지 다른 모든 봇의 거리 배열, 각 종족의 끝) 및 storage, 기본적으로 빈 개체이며 종족 간의 정보를 저장하는 데 사용할 수 있습니다.

예제 봇

팔로어는 턴당 평균 이동량만큼 평균 봇보다 앞서도록 노력할 것입니다.

{
    "Follower": function(dist, energy, bots, storage) {
        storage.turns = storage.turns || 0;

        if (Math.max(...bots))
            storage.avg = ((storage.avg || 0) * storage.turns++ + bots.reduce((a, b, i) => a + (b - storage.last[i]), 0) / bots.length) / storage.turns;

        storage.last = bots;

        return (bots.reduce((a, b) => a + b, 0) / bots.length + (storage.avg || 1)) - dist;
    }
}

제어 장치

// Each bot should be placed in this object

var bot_data = {
    "Follower": function(dist, energy, bots, storage) {
        storage.turns = storage.turns || 0;

        if (Math.max(...bots))
            storage.avg = ((storage.avg || 0) * storage.turns++ + bots.reduce((a, b, i) => a + (b - storage.last[i]), 0) / bots.length) / storage.turns;

        storage.last = bots;

        return (bots.reduce((a, b) => a + b, 0) / bots.length + (storage.avg || 1)) - dist;
    }
};

var games = 0;
var records = {};

// races: Number of races
// log: Array of bot names to log information about, or null for no logging
//   Per-turn logging will only happen in games with less than 10 races
//   Per-race logging will only happen in games with less than 100 races
// bold: Whether to use bold text when logging information

var run_game = function(races, log = [], bold = true) {
    var perf_now = performance.now();
    
    var bots = [];
    
    games++;

    for (let bot in bot_data)
        bots.push({
            name: bot,
            run: bot_data[bot]
        });

    var uids = new Array(bots.length);

    for (let i = 0; i < uids.length; i++)
        uids[i] = i;

    var race = 0;
    var turn = 0;

    for (let r = 0; r < races; r++) {
        race++;

        for (let j, i = 0; i < uids.length; i++) {
            j = Math.random() * (i + 1) | 0;
            [uids[i], uids[j]][uids[j], uids[i]];
        }

        for (let b, i = 0; i < bots.length; i++) {
            b = bots[i];

            bots[i] = {
                name: b.name,
                run: b.run,

                uid: uids[i],
                dist: 0,
                energy: (b.energy || 0) + 100,
                points: b.points || 0,

                storage: b.storage || {},

                next: 0,
                inactive: 0
            };
        }

        turn = 0;

        while ((bots.every(b => b.dist < 100) && bots.some(b => b.energy > 0 && b.inactive < 3))) {
            turn++;

            for (let b, i = 0; i < bots.length; i++) {
                b = bots[i];

                try {
                    b.next = b.run(
                        b.dist, b.energy,
                        bots.filter(o => o.uid != b.uid).map(o => o.dist),
                        b.storage
                    );

                    if (log && log.includes(b.name) && races < 10)
                        console.log("[" + race + ":" + turn + "] " + b.name + "(" + (Math.round(b.dist * 1000) / 1000) + "," + (Math.round(b.energy * 1000) / 1000) + "):", b.next);
                } catch(e) {
                    if (log && races < 10)
                        console.warn("[" + race + ":" + turn + "] " + b.name + ":\n" + (e.stack || e.message));

                    b.next = 0;
                }

                b.next = Number(b.next);

                if (Number.isNaN(b.next))
                    b.next = 0;

                b.next = Math.max(Math.min(b.next, 100 - b.dist, Math.sqrt(b.energy)), 0);

                if (!b.next)
                    b.inactive++;
            }

            for (let b, i = 0; i < bots.length; i++) {
                b = bots[i];

                b.dist += b.next;
                b.energy = Math.max(b.energy - b.next ** 2, 0);
            }
        }

        for (let b, i = 0; i < bots.length; i++) {
            b = bots[i];

            b.energy = b.energy + b.dist;
            b.points += b.dist / 10;
        }

        if (log && races < 100)
            console.log(
                (bold ? "%c" : "") + "Race " + race + ":\n" +
                (bold ? "%c" : "") + bots.map(b => b).sort((a, b) => b.dist - a.dist).map(
                    b => b.name.slice(0, 16) + " ".repeat(20 - Math.min(b.name.length, 16)) + (Math.round(b.dist * 1000) / 10000)
                ).join("\n"), ...(bold ? ["font-weight: bold;", ""] : [])
            );
    }

    for (let i = 0; i < bots.length; i++)
        records[bots[i].name] = (records[bots[i].name] || 0) + bots[i].points / races;
    
    if (log)
        console.log(
            (bold ? "%c" : "") + "Average Points/Race (" + races + " races, " + (Math.ceil((performance.now() - perf_now) * 1000) / 1000) + "ms):\n" +
            (bold ? "%c" : "") + bots.sort((a, b) => b.points - a.points).map(
                b => b.name.slice(0, 16) + " ".repeat(20 - Math.min(b.name.length, 16)) + (Math.round((b.points / races) * 10000) / 10000)
            ).join("\n"), ...(bold ? ["font-weight: bold;", ""] : [])
        );
};

// Print and clear records for average scores

var print_records = function(bold = true) {
    console.log(
        (bold ? "%c" : "") + "Sum of Average Points/Game:\n" +
        (bold ? "%c" : "") + Object.entries(records).sort((a, b) => b[1] - a[1]).map(
            b => b[0].slice(0, 16) + " ".repeat(20 - Math.min(b[0].length, 16)) + (Math.round(b[1] * 10000) / 10000)
        ).join("\n"), ...(bold ? ["font-weight: bold;", ""] : [])
    );
};

var clear_records = function() {
    records = {};
};

// Default race categories

run_game(250);
run_game(750);
run_game(2500);
run_game(7500);
run_game(25000);

print_records();

규칙

  • 3 턴 연속 이동 한 봇이 없으면 레이스가 종료됩니다 (점수는 계속 계산됩니다).

  • 봇에 오류가 발생하면 턴을 잃게됩니다 (즉, 이동하지 않음).

  • 봇은 컨트롤러 또는 다른 봇을 조작하거나 기타 악의적 일 수 없습니다.

  • 봇은 적절한 시간 내에 실행되어야합니다.

  • 봇은 결정적이어야합니다. 봇의 인수에 의해 시드되지 않는 한 임의성이 없음

잡담: https://chat.stackexchange.com/rooms/112222/simple-race-koth

봇 마감일 : 9 월 4 일 금요일 12:00 UTC (08:00 EDT)

답변

3 Moogie Aug 31 2020 at 18:56

보정기

"SubOptimal"에서 파생 된 Compensator는 "Horde / Burst"전략을 명시 적으로 제공하지 않으며, 오히려 처음이 아니었다면 이전 턴에 모든 에너지를 사용하지 않았을 수 있다는 지식을 통해 자연스럽게 보상합니다. 예상보다 더 많은 에너지를 가질 수 있습니다. 이 과도한 에너지 공급을 활용하기 위해이 봇은 초과 에너지의 절반을 사용하여 더 빠른 레이스를 시도하고 강제하지만 나머지 절반은 예비 에너지를 유지하여 여러 레이스에 걸쳐 잉여 에너지의 효과를 확장합니다.

형제 (SubOptimal)보다 약간 더 나은 성능을 보이는 것으로 보이며,이 제출 시점에 다른 모든 봇보다 약간 앞서 있습니다.

{
    "Compensator": function(dist, energy, bots, storage) {
        if ( dist == 0)
        {
          if (storage.targetStartingEnergy == undefined)
          {
            storage.targetStartingEnergy = energy;
            storage.nominalStartingEnergy = energy + 100;
          }
          else
          {
            if (energy <= storage.nominalStartingEnergy)
            {
              storage.targetStartingEnergy = energy;
            }
            else
            {
              storage.targetStartingEnergy = ((energy - storage.nominalStartingEnergy) * 0.5) +  storage.nominalStartingEnergy;
            }
          }

          if (storage.raceNumber == undefined)
          {
            storage.raceNumber = 1;
          }
          else
          {
            storage.raceNumber++;
          }

          storage.energyConsumptionRate = storage.targetStartingEnergy / 100;
        }

        let step = 0;

        if (storage.raceNumber == 1)
        {
          step = 1;
        }
        else
        {
          step = storage.energyConsumptionRate;
        }

        return step;
    }
}
6 Alion Aug 25 2020 at 21:11

속도 제어

{
    "Rate control": function(distanceTravelled, energyLeft, _, s) {
        if (distanceTravelled === 0) {
            for (let i = 100; i > 0; --i) {
                if (10000 / i > energyLeft) {
                    s.travelSpeed = 100 / (i + 1);
                    break;
                }
            }
        }

        return s.travelSpeed;
    }
}

각 라운드는 결승선에 도달하기 위해 모든 에너지를 사용합니다. 이 항목은 턴당 1 개 이상의 에너지 만 사용하는 동시에 항상 끝까지 도달하도록하기 때문에 "느리고 꾸준함"보다 훨씬 좋습니다. 최적화되지 않았지만 여전히 상당히 빠릅니다.

4 Alion Aug 25 2020 at 20:19

천천히 그리고 착실히

{
    "Slow and steady": function() {
        return 1;
    }
}

이 챌린지로 무엇을해야하는지 알아내는 동안베이스 라인 봇. 전혀 적응하지 않으므로 일종의 메타가 발생하면 지속적으로 잃을 수 있습니다.

3 jonatjano Aug 26 2020 at 19:56

미리 계산 된

{
    "precomputed": function(dist, energy, bots, storage) {
        if (dist === 0) {
            let movements = Array.from(new Array(100), _=>1)

            const totalEnergyConsumed = () => movements.reduce((a,c)=>a+c**2,0)
            let currentIndex = 0

            while(totalEnergyConsumed() < energy) {
                movements[currentIndex] += movements[currentIndex + 1]
                movements.splice(currentIndex + 1, 1)
                if (++currentIndex >= movements.length - 1) {
                    currentIndex = 0
                }
            }

            currentIndex = movements.length
            while(totalEnergyConsumed() > energy) {
                if(movements[currentIndex] > 1) {
                    movements[currentIndex]--
                    movements.push(1)
                } else {
                    currentIndex--
                }
            }

            storage.movements = {}
            movements.reduce((a,c)=>{storage.movements[a]=c;return a+c}, 0)
        }
        return storage.movements[dist]
    }
}

사용 가능한 모든 에너지를 사용하면서 전체 레이스에서 거의 동일한 속도로 이동하기 위해 끝까지의 전체 경로를 계산하여 레이스를 시작합니다.

3 Spitemaster Aug 27 2020 at 02:06

구십

{
    "Ninety": function(dist, energy, bots, storage) {
        if (dist === 0) {
            for (let i = 90; i > 0; --i) {
                if (8100 / i > (energy - 10)) {
                    storage.travelSpeed = 90 / (i + 1);
                    break;
                }
            }
        }
        if (dist >= 89) {
            return 1;
        }

        return storage.travelSpeed;
    }
}

라운드 당 9 점을 목표로합니다. 얼마나 잘하는지는 잘 모르겠지만 봇이 그것보다 더 빨리 끝낼 때 점수를 잃을 가능성이 적습니다 (이것이 분기 된 Rate Control과 비교할 때).

3 SomoKRoceS Aug 29 2020 at 03:34

펄스

"Pulse": function(dist, energy, bots, storage) {
    storage.round = storage.round ? storage.round+1 : 1;
    if(storage.round%500==0) return Math.max([...bots])+50
    return Math.floor(Math.sqrt(energy/100))
}

각 단계는 에너지의 1 % 만 사용합니다. 500 턴마다이 순간 1 위의 거리를 차지하고 50 번 더 패스합니다.

3 TheNumberOne Aug 30 2020 at 02:31

잭 인 더 박스

40 번의 동작으로 게임을 이길 때까지 에너지를 절약하여 게임당 평균 동작 수를 줄입니다.

{
    "Jack in the Box": function(dist, energy, bots, storage) {
        if (!dist) {
            if (energy >= 250) {
                storage.speed = energy / 100
            } else {
                storage.speed = .5
            }
        }
        return storage.speed
    }
}

바보

Simpleton은 단지 이기고 싶어합니다 :(

{
    "Simpleton": function(dist, energy, bots, storage) {
        return energy / (100 - dist)
    }
}

확고한

Steady는 매 턴마다 같은 양을하려고하지만 여분의 에너지가있는 것을 좋아하지 않습니다.

{
    "Steady": function(dist, energy, bots, storage) {
        storage.turns = storage.turns || 0
        storage.totalTurns = storage.totalTurns || 0
        storage.games = storage.games || 0
        storage.totalEnergyGained = storage.totalEnergyGained || 0
        storage.previousEnergy = storage.previousEnergy || 0
        if (!dist) {
            if (storage.games == 0) {
                storage.speed = 1
            } else {
                storage.totalTurns += storage.turns
                storage.turns = 0
                storage.speed = Math.sqrt(storage.totalEnergyGained / storage.totalTurns) + storage.previousEnergy / storage.totalTurns
            }
            storage.totalEnergyGained += energy - storage.previousEnergy
            storage.games++
        }
        storage.turns++;
        storage.previousEnergy = Math.max(energy - Math.max(Math.min(storage.speed, 100 - dist, Math.sqrt(energy)), 0) ** 2, 0)
        return storage.speed;
    }
}
3 Moogie Aug 30 2020 at 12:26

차선

다른 레이서에게 영향을 미칠 수있는 방법이없는 최적의 솔루션은 모든 에너지를 사용하여 다음 라운드에서 가장 많은 에너지를 얻고 상대방의 에너지를 거부하기 위해 먼저 완료하도록하는 것입니다. 이를 위해 첫 번째 레이스에서 턴당 1.0 에너지를 소비 한 다음 후속 레이스에서 턴당 2.0 에너지를 소비하면됩니다 (100 거리에서 승리 할 때 추가로 100 에너지가 주어짐).

이는 봇의 에너지 / 레이스 시작시 이동 거리를 계산하고이 값을 저장 한 다음 레이스의 매 턴마다이 값을 반환함으로써 달성 할 수 있습니다.

이제 우리는 상대 효과에 대한 최적의 솔루션을 알고 있으므로 상대가 수행 할 수 있고 다른 사람에게 영향을 줄 수있는 행동을 고려해야합니다. 이 게임에서 유일한 실제 효과는 승자가되어 현재 레이스를 강제 종료 할 수있는 능력입니다. 봇은 에너지를 축적하고 축적 할 수 있기 때문에 에너지 소비를 최소화하고 에너지 생산을 극대화하여 특정 종족에서 많은 점수를 얻을 수있는 기회를 희생하고 대신 한 종족에서 누적 된 점수를 소비하여 다른 봇을 지배하고 승리 할 수 ​​있습니다. 경주. 이 전략은 전반적으로 높은 점수를 얻지는 못하지만 레이스가 100 턴 후에 끝날 것으로 예상하는 봇에게 영향을 미칩니다. 따라서 레이스의 평균 턴 수가 줄어 듭니다. 따라서이 효과를 보완하기 위해이 "허드 버스트"전략을 사용하는 봇의 효과를 에뮬레이트하는 요소를 추가하여 최적 솔루션에서 차선책을 도출합니다.

이 요소는 봇이 다른 모든 봇 전략을 통합 한 다음 분석을 수행하여 요소를 결정하지 않는 한 계산할 수 없습니다. 이것은 실제로 KoTH 도전 정신이 아니며 허용되지 않을 수 있습니다. 따라서이 봇에 대해 간단한 경험적 분석을 수행하여 제출 시점의 요인을 결정하고 이후의 봇이 더 간섭 할 수 있다는 가정하에 제출이 많을수록 요인을 증가시킬 제출 수를 기반으로 스칼라를 추가했습니다.

궁극적으로 공식은 다음과 같습니다.

distance_per_turn = 시작 _ 에너지 / ((race_distance + hoard_burst_factor) * (1.0 + (number_of_bots-number_of_bots_at_submission) * 0.1))

{
    "Suboptimal": function(dist, energy, bots, storage) {
        if ( dist == 0)
        {
          storage.energyConsumptionRate = energy / ((100 + 10) * ( 1.0 + (bots.length - 26) * 0.1 ));
        }
        
        return storage.energyConsumptionRate;
    },
}
3 RedwolfPrograms Aug 26 2020 at 09:02

로빈 후드

{
    "Robin Hood": function(dist, energy, bots, storage) {
        if (!dist)
            storage.move = [
                [100, 1],
                [200, Math.sqrt(192 / 49) - 0.00695],
                [10000 / 49, (100 / 49)]
            ].sort((a, b) => Math.abs(a[0] - energy) - Math.abs(b[0] - energy))[0][1];

        return storage.move;
    }
}

이 봇은 레이스에서 다음 세 가지 작업 중 하나를 수행합니다.

  • 턴당 하나의 유닛 이동 : 각 게임의 첫 번째 레이스에서 필요한 에너지를 모두 확보하기 위해이 작업을 수행합니다.
  • 턴당 두 유닛보다 약간 느리게 이동 : 이 작업은 매 턴마다 수행되며 에너지를 절약하여 다음을 수행 할 수 있습니다.
  • 턴당 2 유닛보다 약간 더 빠르게 이동 : 현재 경쟁자보다 한 턴 더 빠르게 완료 할 수 있으며 이전 승자 몇 명을 간신히 언더컷 할 수 있습니다 (게시 시점에서 비율 제어가 100 분의 1 포인트 앞서 있음).
2 RedwolfPrograms Aug 26 2020 at 01:37

수집기

{
    "Collector": function(dist, energy, bots, storage) {
        if (!dist) {
            if ("turns" in storage) {
                storage.avg = ((storage.avg * Math.max(storage.races++, 0)) + storage.turns) / Math.max(storage.races, 1);
            } else {
                storage.avg = 100;
                storage.races = -1;
            }
            
            storage.turns = 0;
            
            storage.move = (energy >= 10000 / (storage.avg | 0)) ? (100 / (storage.avg | 0)) : 0.5;
        }
        
        storage.turns++;
        
        return storage.move;
    }
}

수집가는 기본적으로 0.5 단위 / 턴의 속도로 이동합니다. 이것은 에너지 수집에 최적입니다. 레이스가 시작될 때 가지고있는 에너지로 평균을 동점 또는 이길 수 있다고 예측하면 그렇게하려고합니다.

현재 속도 제어에 실패하지만 새로운 전략에 더 잘 적응할 수 있습니다.

2 Neil Aug 26 2020 at 06:37

Greedy / Greedier

{
    "Greedy": function(dist, energy, bots, storage) {
        return energy > 100 ? 2 : 1;
    },
    "Greedier": function(dist, energy, bots, storage) {
        return dist + energy > 100 ? 2 : 1;
    },
}

Greedy는 에너지가 100 이상이면 턴당 2 유닛을 이동하고 그렇지 않으면 턴당 1 유닛을 이동합니다. Greedier는 각 끝에 충분한 에너지가있을 것이라고 생각하면 턴당 2 유닛을 이동합니다. 그렇지 않으면 턴당 1 유닛을 이동합니다. 이것은 봇이 가질 수있는 보너스 에너지를 사용하는 것에 대해 제가 생각할 수있는 가장 간단한 방법이었습니다.

2 ATaco Aug 27 2020 at 15:12

계산 된 스프린터

계산 된 스프린터는 현재 연료가 남아있는 상태에서 최대한 빨리 전체 랩을 실행하려고합니다. 미래의 경주를 계획 할만큼 똑똑하지 않은 사람은 달리기 위해 여기에있는 것만으로도 행복합니다.

{
    "Calculated Sprinter": function(dist, energy, bots, storage){
        var remaining = 100-dist;
        var energyLeftPerUnit = energy/remaining;
        return Math.sqrt(energyLeftPerUnit)
    }
}
2 null Aug 27 2020 at 13:35

(신규) 가속

{
    "Accelerate": function(dist, energy, bots, storage) {
        return dist * 0.21 + 0.001;
    },
}

진정하세요, 저는 단지 매우 단순한 봇으로 실험하고 있습니다.

이 봇은 이해하기 매우 쉽습니다. 처음에는 0.001 속도로 실행되고 2 차적으로 가속됩니다.

2 null Aug 27 2020 at 17:56

나는 임의성을 좋아한다

{
    "I love Randomness": function(dist, energy, bots, storage) {
        storage.rand = Math.abs(dist ^ energy ^ storage.rand) + 1;
        return Math.abs(dist ^ energy ^ storage.rand) + 1;
    }
}
2 histocrat Aug 29 2020 at 08:10

서프라이즈 / 타이밍

"Timing": function(dist, energy, bots, storage) {
  storage.turns = storage.turns || 0;
  storage.games = storage.games || 0;
  storage.turns++;
  if(dist == 0) {
      storage.games++;
      estimated_game_length = Math.ceil( storage.turns / storage.games)+2;
      desired_speed = 100 / estimated_game_length;
      max_speed = Math.sqrt( energy / estimated_game_length);
      storage.speed = Math.min(desired_speed, max_speed);       
  }
  if(storage.games < 3)
      return storage.games;
  return storage.speed;
},
"Surprise": function(dist, energy, bots, storage) {
  storage.turns = storage.turns || 0;
  storage.games = storage.games || 0;
  storage.turns++;
  if(dist == 0) {
      storage.games++;
      estimated_game_length = Math.ceil( storage.turns / storage.games);
      desired_speed = 100 / (estimated_game_length - 3);
      max_speed = Math.sqrt( energy / estimated_game_length);
    if(desired_speed <= max_speed) {
      storage.speed = desired_speed;
    }
    else {
      storage.speed = Math.min(2, max_speed);
    }       
  }
  if(storage.games < 3)
       return storage.games;
  return storage.speed;
}

일반적으로 게임이 얼마나 오래 지속되는지에 따라 고정 속도를 계산합니다. 그런 다음 타이밍은 목표를 맞추려고 시도하고 Surprise는이를이기려고합니다.

이 두 가지를 모두 사용하여 테스트를 실행하는 동안이 KotH의 공모에 대한 규칙이 필요하다는 것이 분명해졌습니다. Surprise는 자신의 점수를 희생하여 레이스 길이를 줄임으로써 Timing이 훨씬 더 잘할 수 있도록 만들 수 있으며, Timing이 알고있는 고정 된 간격으로 만 그렇게함으로써 더 많은 도움을 줄 수 있습니다.

나는 그들이 정신에 있지 않다고 생각하기 때문에 지금 이러한 헛소리를 당기지 않습니다.

1 RedwolfPrograms Aug 27 2020 at 20:37

모방

{
    "Mimic": function(dist, energy, bots, storage) {
        if (!dist) {
            storage.last = bots;
            storage.rand = energy ** 3;
            
            return energy / (100 - dist);
        }
        
        storage.rand = Math.abs(dist ^ dist ** 2 ^ energy ^ energy ** 3 ^ energy ** 5 ^ bots.reduce((s, b) => s + b, 0) ^ storage.rand * (2 ** 31)) / (2 ** 31);
        
        var result = bots.map((b, i) => b - storage.last[i])[storage.rand * bots.length | 0]; // Fix RNG
        
        storage.last = bots;
        
        return Math.max(Math.min(result, Math.sqrt(energy / ((100 - dist) / 4))), Math.sqrt(energy / ((100 - dist))));
    }
}

마지막 턴에 다른 모든 봇의 (효과적인) 이동 목록을 만들고 HighlyRadioactive의 PRNG의 더 나은 버전을 사용하여 의사 난수를 선택합니다. 이 값이 특정 범위 (약 절반 정도 발생) 내에 있는지 확인하므로 어리석은 일을하지 않습니다.

1 null Aug 26 2020 at 17:18

빠르고 안정적이지 않음

{
    "Fast and not steady": function() {
        return 99999999;
    }
}
1 null Aug 28 2020 at 21:00

느린 것보다 빠름

{
    "Faster than Slow": function() {
        return 2;
    }
}

이것이 나쁜 봇이라고 생각한다면 아니오.

Faster than Slow 48.951

1 alexberne Aug 29 2020 at 16:38

전부의

Whole은 분수 거리를 좋아하지 않으며 항상 정수 거리를 이동합니다.

    "Whole": function(dist, energy, bots, storage) {
        if (dist == 0) {
            if (energy < 110) {
                storage.lambda = function(distance) {return 100 - distance - 1;}
                storage.step = 1
            }
            else {
                storage.lambda = function(distance) {return 200 - distance - 2;}
                storage.step = 2
            }
        }
        let expEnergyPast = storage.lambda(dist);
        if (expEnergyPast + (storage.step + 1) ** 2 <= energy) {
            return storage.step + 1;
        }
        return storage.step;
    }

```
1 alexberne Aug 29 2020 at 16:48

사십 구

Fourty-Nine은 Winner & Winner2를 살펴본 결과 49 턴에서 승리하는 것이 48 턴보다 낫다는 것을 인식했습니다. 하지만 Fourty-Nine은 당신의 규칙에 따라 이기기를 원합니다. 따라서 Fourty-Nine은 많은 레이스에서 우승하기 위해 거리를 희생하지 않습니다. 그러나 승리하기 위해 49 턴보다 빨리 갈 수는 없습니다.

    "fourty-nine": function(dist, energy, bots, storage) {
        if (dist == 0) {
            if (energy < 110) {
                storage.step = 1
            }
            else if(energy < 10000.0/49){
                storage.step = 2
            }
            else {
                storage.step = 100.0/49
            }
        }
        return storage.step;
    },
1 RedwolfPrograms Aug 30 2020 at 03:17

예언자

{
    "Predictor": function(dist, energy, bots, storage) {
        if (!dist)
            if (energy == 100)
                storage.move = 1;
            else
                storage.move = (energy >= 10000 / (50 - bots.length * 0.25 | 0)) ? (100 / (50 - bots.length * 0.25 | 0)) : 1.3;

        return storage.move;
    }
}

Predictor는 더 많은 봇이 추가 될수록 더 빨리 승리해야한다고 가정합니다. 시간이 지남에 따라 에너지를 수집 한 다음 상자 속의 수집기 또는 잭과 유사한 방식으로 결승선을 향해 질주합니다.

1 NoOorZ24 Sep 04 2020 at 17:07

결정 3M8

다르게 작동 하는 UWUTM8의 개선

누군가가 과속을 할 때를 예측하고 더 많은 포인트를 얻기 위해 더 많은 에너지를 사용하려고합니다.

"DECISION3M8": function(dist, energy, bots, storage) {
    const checkpointPer = 5;
    if (storage.turn == undefined) {
        storage.turn = 0;
    } else {
        storage.turn = storage.turn + 1;
    }
    
    if (dist === 0) {
        if (storage.round == undefined) {
            storage.round = 0;
        }
        storage.round = storage.round + 1;
        storage.turn = 0;
        storage.maxAtPreviouscheckpoint = 0;
        storage.predictedTurnsLeft = 100;
        storage.travelSpeed = Math.sqrt(energy / 50);
        
        if (energy == 100) {
            return 1;
        }
    } else if (storage.turn % checkpointPer == 0) {
        let maxAtCurrentTurn = Math.max( ...bots );
        let speederCheck = maxAtCurrentTurn / (storage.turn / checkpointPer) - storage.maxAtPreviouscheckpoint / ((storage.turn / checkpointPer) - 1);
        let speedOfSpeederPerTurn = maxAtCurrentTurn / storage.turn;
        if ((Math.abs(speederCheck) < 0.01) && (maxAtCurrentTurn > dist)) {
            //console.log(speederCheck);
            storage.predictedTurnsLeft = Math.ceil(100 / speedOfSpeederPerTurn) - (100 - storage.turn);
            storage.travelSpeed = Math.sqrt(energy / (storage.turn - speedOfSpeederPerTurn));
            //console.log(storage.predictedTurnsLeft);
        }
    }
    
    return storage.travelSpeed;
}
alexberne Aug 29 2020 at 16:43

우승자

우승자는 당신의 규칙에 관심이 없습니다. 승자는 자체 규칙에 따라 플레이합니다.

우승자는 가능한 한 많은 레이스에서이기려고합니다 (= 거리 100에서 끝남).

"Winner": function(dist, energy, bots, storage) {
        if (dist == 0) {
            if (energy < 10000.0/49) {
                storage.step= 0.5;
            }
            else {
                storage.step = 100.0/49;
            }
        }
        return storage.step;
    },
"Winner2": function(dist, energy, bots, storage) {
        if (dist == 0) {
            if (energy < 10000.0/48) {
                storage.step= 0.5;
            }
            else {
                storage.step = 100.0/48;
            }
        }
        return storage.step;
    },

```
NoOorZ24 Sep 03 2020 at 13:47

UWUTM8

많은 봇과 마찬가지로 가능한 한 머치 에너지를 사용하여 최대한 빨리 끝내려고합니다. 또한 특정 점수에서 전혀 끝내려고하지 않고 9 점에 도달하려고합니다.

"UWUTM8": function(dist, energy, bots, storage) {
    if (dist === 0) {
        if (storage.round == undefined) {
            storage.round = 0;
        }
        storage.round = storage.round + 1;
        if (storage.round % 2500 == 0 || storage.round == 250 || storage.round == 750) {
            storage.travelSpeed = Math.sqrt(energy / 90)
        } else {
            storage.travelSpeed = Math.sqrt(energy / 100)
        }
    }

    return storage.travelSpeed;
}