Prosty wyścig KoTH

Aug 25 2020

To wyzwanie oficjalnie się zakończyło. Kolejne zgłoszenia nie będą konkurencyjne (ale nadal są mile widziane!). Zobacz wyniki tutaj

W tym wyzwaniu zgłoszenia („boty”) powinny być funkcjami Javascript, które starają się wygrać jak najwięcej wyścigów. W każdym wyścigu boty będą miały ograniczoną ilość energii, którą należy wykorzystać do jak najszybszego przemieszczenia się na odległość 100 jednostek.

Mechanika

Każda gra będzie składać się z kilku wyścigów, które składają się z pewnej liczby tur. W każdej turze boty będą wybierać nieujemną odległość do pokonania do przodu. Ilość zużytej energii jest równa kwadratowi przebytej odległości (a boty będą podróżować tylko na tyle, na ile pozwala ich poziom energii, jeśli powrócą na większą odległość).

Na początku każdego wyścigu pozycje botów są resetowane do 0. Gdy jeden lub więcej botów osiągnie odległość 100 jednostek, wyścig dobiega końca. Zwróć uwagę, że jeśli bot zwróci odległość, która umieściłaby go dalej niż 100 jednostek od początku, przesunie się tylko tak daleko, jak potrzebuje, aby wygrać. Na początku wszystkich wyścigów boty otrzymają 100 energii oprócz pozostałej energii z ostatniego wyścigu. Otrzymają również premię w wysokości 1 energii za każdą jednostkę, którą podróżowali w poprzednim wyścigu.

Zwrotnica

Pod koniec każdego wyścigu wszystkie boty otrzymają jeden punkt za każde dziesięć jednostek, które przejechały po zakończeniu gry (wliczając ułamki punktu). Na koniec gry wygrywa bot z największą liczbą punktów.

Ponieważ prawdopodobnie boty zostaną zoptymalizowane pod kątem określonej liczby wyścigów, będzie 5 kategorii, w których będą rywalizować wszystkie boty: 250 wyścigów, 750 wyścigów, 2500 wyścigów, 7500 wyścigów i 25000 wyścigów. Ogólny wynik bota będzie sumą ich średniego wyniku na wyścig w każdej z tych kategorii, dając najwyższy możliwy wynik 50.

Wejście wyjście

Boty otrzymają następujące argumenty: dist(odległość, jaką przebyły w bieżącym wyścigu), energy(ilość posiadanej energii), bots(tablica odległości wszystkich innych botów na koniec ostatniej tury, przetasowana na koniec każdego wyścigu) i storage, który jest domyślnie pustym obiektem i może być używany do przechowywania informacji między wyścigami.

Przykładowy bot

Follower będzie starał się wyprzedzić przeciętnego bota o średnią wartość ruchu na turę.

{
    "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;
    }
}

Kontroler

// 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();

Zasady

  • Jeśli żadne boty nie poruszały się przez trzy tury z rzędu, wyścig się zakończy (wyniki będą nadal liczone)

  • Jeśli bot się pomyli, straci turę (tj. Nie poruszy się)

  • Boty nie mogą manipulować kontrolerem ani innymi botami ani być w inny sposób złośliwe

  • Boty powinny działać w rozsądnym czasie

  • Boty muszą być deterministyczne; brak losowości, chyba że jest zapoczątkowana przez argumenty bota

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

Termin dostawy botów: piątek, 4 września, 12:00 UTC (08:00 EDT)

Odpowiedzi

3 Moogie Aug 31 2020 at 18:56

Kompensator

Pochodna od „SubOptimal”, Compensator nie obsługuje wyraźnie strategii „Horde / Burst”, raczej naturalnie kompensuje, wiedząc, że jeśli nie był pierwszy, to mógł nie wykorzystać całej swojej energii w poprzedniej turze może mieć więcej energii niż oczekiwano. Aby wykorzystać tę nadwyżkę energii, ten bot zużyje połowę nadwyżki energii, aby spróbować zmusić do szybszego wyścigu, ale pozostawiając drugą połowę w rezerwie, aby spróbować rozciągnąć efekt nadwyżki energii na wiele ras.

Wydaje się, że działa nieco lepiej niż jego siostra (SubOptimal), a w momencie tego zgłoszenia nieznacznie wyprzedza wszystkie inne boty.

{
    "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

Kontrola tempa

{
    "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;
    }
}

Każda runda zużywa całą swoją energię, aby dotrzeć do mety. Ściśle lepiej niż „Wolno i stabilnie”, ponieważ ten wpis będzie zużywał tylko 1 lub więcej energii na turę, jednocześnie upewniając się, że zawsze dotrzesz do końca. Niezoptymalizowany, ale nadal dość szybki.

4 Alion Aug 25 2020 at 20:19

Powoli i spokojnie

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

Baseline bot, kiedy próbuję wymyślić, co zrobić z tym wyzwaniem. W ogóle się nie dostosowuje, więc może zacząć konsekwentnie tracić, jeśli rozwinie się jakaś meta.

3 jonatjano Aug 26 2020 at 19:56

wstępnie obliczone

{
    "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]
    }
}

rozpoczyna wyścig, obliczając pełną ścieżkę do końca, aby poruszać się z prawie taką samą prędkością przez cały wyścig, jednocześnie wykorzystując całą dostępną energię

3 Spitemaster Aug 27 2020 at 02:06

Dziewięćdziesiąt

{
    "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;
    }
}

Ma na celu zdobycie 9 punktów za rundę. Nie jestem pewien, jak dobrze to działa, ale jest mniej prawdopodobne, że stracę punkty, gdy boty kończą szybciej niż to (w porównaniu do Rate Control, z której jest to rozwidlone).

3 SomoKRoceS Aug 29 2020 at 03:34

Puls

"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))
}

Każdy krok zużywa tylko 1% energii. Co 500 tur pokonuje dystans pierwszego miejsca w tym momencie i dodaje 50 zaliczeń.

3 TheNumberOne Aug 30 2020 at 02:31

Jack in the Box

Oszczędza energię, dopóki nie będzie w stanie pokonać gry w 40 ruchach, zmniejszając średnią liczbę ruchów na grę.

{
    "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
    }
}

Prostak

Prosty chce tylko wygrać :(

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

Stały

Steady stara się przejść tę samą wartość co turę, ale nie lubi mieć dodatkowej energii.

{
    "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

Nieoptymalne

Optymalnym rozwiązaniem, w którym nie ma sposobu, aby wpłynąć na innych zawodników, jest wykorzystanie całej swojej energii, aby upewnić się, że skończysz jako pierwszy, aby zdobyć jak najwięcej energii w następnej rundzie i odmówić energii swoim przeciwnikom. Można to osiągnąć, wydając 1,0 energii na turę w pierwszym wyścigu, a następnie 2,0 na turę na kolejne wyścigi (ze względu na dodatkowe 100 energii na wygraną na dystansie 100)

Można to osiągnąć, obliczając energię bota / odległość do przebycia na początku wyścigu, zapisując tę ​​wartość, a następnie zwracając ją po każdej turze wyścigu.

Teraz, gdy znamy optymalne rozwiązanie bez efektów przeciwnika, musimy wziąć pod uwagę działania, które mogą wykonać przeciwnicy, a które mogą wpłynąć na innych. W tej grze jedynym rzeczywistym efektem jest możliwość wymuszenia zakończenia obecnego wyścigu poprzez wygranie. Ponieważ boty mogą gromadzić i gromadzić energię, mogą zdecydować się na zminimalizowanie zużycia energii i zmaksymalizowanie jej produkcji, poświęcając szansę na zdobycie wielu punktów dla dowolnej rasy i zamiast tego wydać zgromadzone punkty w jednym wyścigu, aby zdominować inne boty i wygrać to. wyścigi. Chociaż ta strategia nie daje ogólnie wysokich punktów, ma wpływ na boty, które oczekują, że wyścigi zakończą się po 100 turach. W ten sposób zmniejsza się średnia liczba zakrętów w wyścigu. Aby zrekompensować ten efekt, rozwiązanie nieoptymalne jest wyprowadzane z rozwiązania optymalnego przez dodanie współczynnika, który naśladuje działanie botów, które używają tej strategii „hord-burst”.

Ten czynnik nie jest w stanie obliczyć, chyba że bot włączy wszystkie inne strategie bota, a następnie przeprowadzi analizę w celu określenia czynnika. To nie jest w duchu wyzwań KoTH i może być niedozwolone. Tak więc w przypadku tego bota przeprowadzono prostą analizę empiryczną w celu określenia współczynnika w momencie składania i dodania skalara opartego na liczbie zgłoszeń, który zwiększy współczynnik wraz z większą liczbą zgłoszeń przy założeniu, że później boty mogą bardziej przeszkadzać.

Ostatecznie formuła jest następująca:

distance_per_turn = start_energy / ((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

{
    "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;
    }
}

Ten bot wykona jedną z trzech rzeczy w wyścigu:

  • Przenieś jedną jednostkę na turę: odbywa się to w pierwszym wyścigu w każdej grze, aby zapewnić pełne 200 potrzebnej energii
  • Poruszaj się nieco wolniej niż dwie jednostki na turę: Odbywa się to co drugą turę i oszczędza tyle energii, aby pozwolić jej ...
  • Poruszaj się nieco szybciej niż dwie jednostki na turę: Dzięki temu może ukończyć o jedną turę szybciej niż obecni konkurenci i ledwo podciąga kilku poprzednich zwycięzców (chociaż Kontrola tempa jest o jedną setną punktu od wysłania)
2 RedwolfPrograms Aug 26 2020 at 01:37

Kolektor

{
    "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;
    }
}

Kolekcjoner będzie domyślnie poruszał się z prędkością 0,5 jednostki / turę. Jest to optymalne do gromadzenia energii. Jeśli na początku wyścigu przewiduje, że może zrównać lub pobić średnią swoją energią, to spróbuje to zrobić.

Obecnie traci kontrolę nad kursem, ale może być w stanie lepiej dostosować się do nowych strategii.

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;
    },
}

Chciwy poruszy 2 jednostki / turę, jeśli ma więcej niż 100 energii, w przeciwnym razie 1 jednostkę / turę. Greedier przesunie się o 2 jednostki / turę, jeśli uważa, że ​​prawdopodobnie będzie miał wystarczająco dużo energii na każdy koniec, w przeciwnym razie 1 jednostkę / turę. To były najprostsze sposoby, jakie mogłem wymyślić, aby użyć dowolnej dodatkowej energii, jaką mógł mieć bot.

2 ATaco Aug 27 2020 at 15:12

Obliczony Sprinter

Wyliczony Sprinter stara się przebiec pełne okrążenie tak szybko, jak tylko może, przy obecnym paliwie. Nie jest na tyle sprytny, by planować przyszłe wyścigi, jest po prostu szczęśliwy, że może tu pobiegać.

{
    "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

(Nowość) Przyspiesz

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

Uspokój się, po prostu eksperymentuję z niezwykle prostymi botami.

Ten bot jest bardzo łatwy do zrozumienia. Początkowo działa z prędkością 0,001 i przyspiesza kwadratowo.

2 null Aug 27 2020 at 17:56

Uwielbiam losowość

{
    "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

Niespodzianka / wyczucie czasu

"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;
}

Oblicz stałą prędkość na podstawie tego, jak długo trwają gry. Czas próbuje trafić w sedno, podczas gdy Niespodzianka próbuje go pokonać.

Podczas przeprowadzania testów z obydwoma okazało się, że prawdopodobnie potrzebujemy reguł dotyczących zmowy w tym KotH, chociaż interakcje są minimalne. Zaskoczenie może sprawić, że Timing będzie znacznie lepszy, poświęcając swój własny wynik, aby skrócić długość wyścigu, i może mu pomóc jeszcze bardziej, robiąc to tylko w ustalonych odstępach czasu, o których wie.

Nie robię teraz tych shenaniganów, ponieważ zakładam, że nie są w duchu.

1 RedwolfPrograms Aug 27 2020 at 20:37

Imitować

{
    "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))));
    }
}

Tworzy listę wszystkich (efektywnych) ruchów bota w ostatniej turze i wybiera pseudolosowy, używając lepszej wersji PRNG HighlyRadioactive. Zapewnia, że ​​te wartości mieszczą się w pewnym zakresie (co zdarza się mniej więcej w połowie przypadków), więc nie robi nic głupiego.

1 null Aug 26 2020 at 17:18

Szybko i niestabilnie

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

Szybciej niż wolno

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

Jeśli uważasz, że to zły bot, to nie.

Faster than Slow 48.951

1 alexberne Aug 29 2020 at 16:38

Cały

Całość nie lubi ułamków odległości i zawsze przesunie odległość będącą liczbą całkowitą.

    "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

Czterdzieści dziewięć

Fourty-Nine spojrzał na Winner & Winner2 i uznał, że 49 tur na zwycięstwo to lepsze niż 48 tur na zwycięstwo. Ale Fourty-Nine chce wygrać zgodnie z twoimi zasadami. Tak więc Fourty-Nine nie poświęca swojego średniego dystansu, aby wygrać wiele wyścigów. Ale wygrana nigdy nie będzie szybsza niż 49 tur.

    "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

Urządzenie prognozujące

{
    "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 zakłada, że ​​im więcej botów jest dodanych, tym szybciej musi jechać, aby wygrać. Zbiera energię w czasie, a następnie biegnie do mety w sposób podobny do Kolekcjonera lub Jacka w pudełku.

1 NoOorZ24 Sep 04 2020 at 17:07

DECYZJA3M8

Ulepszenie UWUTM8, które działa trochę inaczej

Próbuje przewidzieć, kiedy ktoś przekracza prędkość, i próbuje zużyć więcej energii, aby zdobyć więcej punktów

"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

Zwycięzca

Zwycięzca nie dba o Twoje zasady. Zwycięzca gra według własnych zasad.

Zwycięzca próbuje wygrać (= zakończyć na dystansie 100) w jak największej liczbie wyścigów.

"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

Podobnie jak wiele botów, staram się skończyć tak szybko, jak to możliwe, używając jak największej ilości energii. Nie próbuje też w ogóle kończyć na określonych znakach, ale stara się osiągnąć 9 punktów

"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;
}