Einfaches Rennen KoTH

Aug 25 2020

Diese Herausforderung ist offiziell beendet. Weitere Einsendungen sind nicht wettbewerbsfähig (aber dennoch willkommen!). Sehen Sie sich hier die Ergebnisse an

Bei dieser Herausforderung sollten Einsendungen ("Bots") Javascript-Funktionen sein, die versuchen, so viele Rennen wie möglich zu gewinnen. In jedem Rennen haben Bots eine begrenzte Energiemenge, die verwendet werden sollte, um über eine Distanz von 100 Einheiten so schnell wie möglich zu reisen.

Mechanik

Jedes Spiel besteht aus mehreren Rennen, die aus mehreren Runden bestehen. In jeder Runde wählen die Bots eine nicht negative Entfernung, um vorwärts zu reisen. Die verbrauchte Energiemenge entspricht dem Quadrat der zurückgelegten Strecke (und Bots bewegen sich nur so weit, wie es ihr Energieniveau zulässt, wenn sie eine längere Strecke zurücklegen).

Zu Beginn jedes Rennens werden die Positionen der Bots auf 0 zurückgesetzt. Sobald ein oder mehrere Bots eine Entfernung von 100 Einheiten erreicht haben, endet das Rennen. Beachten Sie, dass sich ein Bot nur so weit bewegt, wie er benötigt, um zu gewinnen, wenn er eine Distanz zurückgibt, die ihn von Anfang an weiter als 100 Einheiten platziert. Zu Beginn aller Rennen erhalten Bots zusätzlich zu den verbleibenden Resten des letzten Rennens 100 Energie. Sie erhalten außerdem einen Bonus von 1 Energie für jede Einheit, die sie im vorherigen Rennen bereist haben.

Punkte

Am Ende jedes Rennens erhalten alle Bots einen Punkt für jeweils zehn Einheiten, die sie am Ende des Spiels zurückgelegt haben (einschließlich Bruchteilen eines Punktes). Am Ende eines Spiels gewinnt der Bot mit den meisten Punkten.

Da es wahrscheinlich ist, dass Bots für eine bestimmte Anzahl von Rennen optimiert werden, gibt es 5 Kategorien, in denen alle Bots gegeneinander antreten: 250 Rennen, 750 Rennen, 2500 Rennen, 7500 Rennen und 25000 Rennen. Die Gesamtpunktzahl eines Bots ist die Summe seiner durchschnittlichen Punktzahl pro Rennen in jeder dieser Kategorien, wodurch die höchstmögliche Punktzahl 50 erreicht wird.

Input-Output

Bots erhalten die folgenden Argumente: dist(die Entfernung, die sie im aktuellen Rennen zurückgelegt haben), energy(die Energiemenge, die sie haben), bots(eine Reihe der Entfernungen aller anderen Bots zum Ende der letzten Runde, gemischt am Ende jedes Rennens), und storage, das standardmäßig ein leeres Objekt ist und zum Speichern von Informationen zwischen Rennen verwendet werden kann.

Beispiel bot

Der Follower wird versuchen, dem durchschnittlichen Bot um den durchschnittlichen Betrag pro Spielzug einen Schritt voraus zu sein.

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

Regler

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

Regeln

  • Wenn sich drei Runden hintereinander keine Bots bewegt haben, endet ein Rennen (die Punkte werden weiterhin gezählt).

  • Wenn ein Bot Fehler macht, verliert er eine Runde (dh nicht bewegen)

  • Bots dürfen den Controller oder andere Bots nicht manipulieren oder auf andere Weise böswillig sein

  • Bots sollten in angemessener Zeit ausgeführt werden

  • Bots müssen deterministisch sein; Keine Zufälligkeit, es sei denn, sie wird durch die Argumente des Bots festgelegt

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

Bots fällig bis: Freitag, 4. September, 12:00 UTC (08:00 EDT)

Antworten

3 Moogie Aug 31 2020 at 18:56

Kompensator

Compensator ist ein Derivat von "SubOptimal" und berücksichtigt nicht explizit die "Horde / Burst" -Strategie, sondern kompensiert natürlich durch das Wissen, dass es in der vorherigen Runde möglicherweise nicht seine gesamte Energie verbraucht hat, wenn es nicht das erste war Es kann mehr Energie als erwartet haben. Um von dieser Überversorgung mit Energie zu profitieren, wird dieser Bot die Hälfte der überschüssigen Energie verwenden, um ein schnelleres Rennen zu erzwingen. Die andere Hälfte wird jedoch in Reserve gehalten, um die Auswirkungen des Energieüberschusses auf mehrere Rennen auszudehnen.

Es scheint etwas besser zu funktionieren als sein Geschwister (SubOptimal) und liegt zum Zeitpunkt dieser Einreichung etwas vor allen anderen Bots.

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

Ratenkontrolle

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

Jede Runde verbraucht ihre gesamte Energie, um die Ziellinie zu erreichen. Streng besser als "Langsam und stetig", da dieser Eintrag immer nur 1 oder mehr Energie pro Runde verbraucht und gleichzeitig sicherstellt, dass er immer bis zum Ende reicht. Nicht optimiert, aber immer noch ziemlich schnell.

4 Alion Aug 25 2020 at 20:19

Langsam und stetig

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

Baseline Bot, während ich versuche herauszufinden, was ich mit dieser Herausforderung anfangen soll. Passt sich überhaupt nicht an, so dass es möglicherweise ständig verliert, wenn sich eine Art Meta entwickelt.

3 jonatjano Aug 26 2020 at 19:56

vorberechnet

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

Startet das Rennen, indem der gesamte Weg bis zum Ende berechnet wird, um während des gesamten Rennens fast die gleiche Geschwindigkeit zu erreichen und dabei die gesamte verfügbare Energie zu verbrauchen

3 Spitemaster Aug 27 2020 at 02:06

Neunzig

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

Ziel ist es, 9 Punkte pro Runde zu erhalten. Ich bin mir nicht sicher, wie gut es funktioniert, aber es ist weniger wahrscheinlich, dass Punkte an Bots verloren gehen, die schneller abschließen als es (im Vergleich zu Rate Control, aus dem dies hervorgeht).

3 SomoKRoceS Aug 29 2020 at 03:34

Impuls

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

Jeder Schritt verbraucht nur 1% der Energie. Alle 500 Umdrehungen nimmt der Abstand zum ersten Platz in diesem Moment und addiert 50 Pass.

3 TheNumberOne Aug 30 2020 at 02:31

Springteufel

Spart Energie, bis das Spiel in 40 Zügen geschlagen werden kann, wodurch die Anzahl der durchschnittlichen Züge pro Spiel verringert wird.

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

Einfaltspinsel

Simpleton will nur gewinnen :(

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

Stetig

Stetig versucht, in jeder Runde die gleiche Menge zu erreichen, mag es aber nicht, zusätzliche Energie zu haben.

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

Suboptimal

Die optimale Lösung, bei der es keine Möglichkeit gibt, andere Rennfahrer zu beeinflussen, besteht darin, Ihre gesamte Energie zu nutzen, um sicherzustellen, dass Sie als Erster abschließen, um in der nächsten Runde die meiste Energie zu gewinnen und Ihren Gegnern Energie zu verweigern. Dies kann erreicht werden, indem 1,0 Energie pro Runde für das erste Rennen und dann 2,0 Energie pro Runde für nachfolgende Rennen ausgegeben werden (aufgrund der zusätzlichen 100 Energie, die für den Gewinn auf 100 Distanz gegeben werden).

Dies kann erreicht werden, indem die Energie des Bots / die Entfernung zu Beginn eines Rennens berechnet wird, dieser Wert gespeichert wird und dieser Wert dann in jeder Runde des Rennens zurückgegeben wird.

Nachdem wir nun die optimale Lösung ohne gegnerische Effekte kennen, müssen wir die Aktionen berücksichtigen, die Gegner ausführen können und die andere beeinflussen können. In diesem Spiel ist der einzige wirkliche Effekt die Fähigkeit, das Ende des aktuellen Rennens zu erzwingen, indem man der Gewinner ist. Da Bots Energie horten und sammeln dürfen, können sie den Energieverbrauch minimieren und die Energieerzeugung maximieren, indem sie die Chance opfern, viele Punkte für ein bestimmtes Rennen zu sammeln, und stattdessen die gesammelten Punkte in einem Rennen ausgeben, um die anderen Bots zu dominieren und diese zu gewinnen Rennen. Diese Strategie bringt zwar insgesamt keine Höhepunkte, wirkt sich jedoch auf Bots aus, die erwarten, dass die Rennen nach 100 Runden beendet werden. Die durchschnittliche Anzahl der Runden eines Rennens wird somit reduziert. Um diesen Effekt zu kompensieren, wird aus der optimalen Lösung eine suboptimale Lösung abgeleitet, indem ein Faktor hinzugefügt wird, der den Effekt von Bots emuliert, die diese "Hoard-Burst" -Strategie verwenden.

Dieser Faktor kann nur berechnet werden, wenn der Bot alle anderen Bot-Strategien einbezieht und anschließend eine Analyse durchführt, um den Faktor zu bestimmen. Dies ist nicht wirklich im Sinne von KoTH-Herausforderungen und möglicherweise nicht erlaubt. Daher wurde für diesen Bot eine einfache empirische Analyse durchgeführt, um den Faktor zum Zeitpunkt der Übermittlung zu bestimmen und einen Skalar basierend auf der Anzahl der Übermittlungen hinzuzufügen, der den Faktor als mehr Übermittlungen erhöht, unter der Annahme, dass spätere Bots möglicherweise störender sind.

Letztendlich lautet die Formel:

distance_per_turn = Starting_energy / ((Race_distance + Hoard_burst_factor) * (1.0 + (Anzahl_der_Bots - Anzahl_der_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;
    }
}

Dieser Bot wird eines von drei Dingen in einem Rennen tun:

  • Bewege eine Einheit pro Spielzug: Dies geschieht im ersten Rennen eines jeden Spiels, um sicherzustellen, dass es die vollen 200 Energie hat, die es benötigt
  • Bewegen Sie sich etwas langsamer als zwei Einheiten pro Runde: Dies geschieht in jeder zweiten Runde und spart gerade genug Energie, um ...
  • Bewegen Sie sich etwas schneller als zwei Einheiten pro Runde: Damit kann es eine Runde schneller als die aktuellen Konkurrenten beenden und einige der vorherigen Gewinner kaum unterbieten (obwohl die Ratenkontrolle zum Zeitpunkt der Veröffentlichung um einen Hundertstelpunkt voraus ist).
2 RedwolfPrograms Aug 26 2020 at 01:37

Kollektor

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

Der Sammler bewegt sich standardmäßig mit einer Geschwindigkeit von 0,5 Einheiten / Umdrehung. Dies ist optimal zum Sammeln von Energie. Wenn es zu Beginn eines Rennens vorhersagt, dass es den Durchschnitt mit der Energie, die es hat, binden oder übertreffen kann, wird es versuchen, dies zu tun.

Verliert derzeit die Zinskontrolle, kann sich aber möglicherweise besser an neue Strategien anpassen.

2 Neil Aug 26 2020 at 06:37

Gierig / gieriger

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

Gierig bewegt sich 2 Einheiten / Runde, wenn es mehr als 100 Energie hat, sonst 1 Einheit / Runde. Greedier bewegt 2 Einheiten pro Runde, wenn er glaubt, dass er wahrscheinlich genug Energie für jedes Ende hat, andernfalls 1 Einheit pro Runde. Dies waren die einfachsten Möglichkeiten, die ich mir vorstellen konnte, um die Bonus-Energie des Bots zu nutzen.

2 ATaco Aug 27 2020 at 15:12

Berechneter Sprinter

Der berechnete Sprinter versucht, die volle Runde so schnell wie möglich mit seinem aktuellen Kraftstoff zu fahren. Nicht klug genug, um zukünftige Rennen zu planen, ist einfach froh, für den Lauf hier zu sein.

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

(Neu) Beschleunigen

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

Beruhige dich, ich experimentiere nur mit extrem einfachen Bots.

Dieser Bot ist sehr leicht zu verstehen. Es läuft zunächst mit einer Geschwindigkeit von 0,001 und beschleunigt quadratisch.

2 null Aug 27 2020 at 17:56

Ich liebe Zufälligkeit

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

Überraschung / Timing

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

Berechnen Sie eine feste Geschwindigkeit basierend darauf, wie lange Spiele im Allgemeinen dauern. Timing versucht dann, die Marke zu treffen, während Surprise versucht, sie zu schlagen.

Bei der Durchführung von Tests mit beiden wurde deutlich, dass wir in diesem KotH wahrscheinlich Regeln für Absprachen benötigen, die jedoch minimal sind. Überraschung könnte dazu führen, dass Timing viel besser abschneidet, indem es seine eigene Punktzahl opfert, um die Rennlänge zu verkürzen, und es könnte noch mehr helfen, indem es dies nur in festgelegten Intervallen tut, die Timing kennt.

Ich ziehe diese Shenanigans jetzt nicht, weil ich annehme, dass sie nicht im Geiste sind.

1 RedwolfPrograms Aug 27 2020 at 20:37

Nachahmen

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

Erstellt eine Liste aller (effektiven) Bewegungen aller anderen Bots in der letzten Runde und wählt eine pseudozufällige mit einer besseren Version von HighlyRadioactives PRNG aus. Es stellt sicher, dass diese Werte innerhalb eines bestimmten Bereichs liegen (was ungefähr die Hälfte der Zeit vorkommt), sodass es nichts Dummes tut.

1 null Aug 26 2020 at 17:18

Schnell und nicht stabil

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

Schneller als langsam

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

Wenn Sie denken, dass dies ein schlechter Bot ist, dann nein.

Faster than Slow 48.951

1 alexberne Aug 29 2020 at 16:38

Ganze

Whole mag keine gebrochenen Entfernungen und bewegt immer eine Entfernung, die eine ganze Zahl ist.

    "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

Neunundvierzig

Neunundvierzig warf einen Blick auf Winner & Winner2 und erkannte, dass 49 Runden zu gewinnen besser sind als 48 Runden zu gewinnen. Aber Neunundvierzig will nach deinen Regeln gewinnen. Fourty-Nine opfert also nicht seine durchschnittliche Distanz, um viele Rennen zu gewinnen. Aber es wird nie schneller als 49 Runden gehen, um zu gewinnen.

    "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

Anzeichen

{
    "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 geht davon aus, dass je mehr Bots hinzugefügt werden, desto schneller muss es gehen, um zu gewinnen. Es sammelt im Laufe der Zeit Energie und sprintet dann auf ähnliche Weise wie Collector oder Jack in the Box zur Ziellinie.

1 NoOorZ24 Sep 04 2020 at 17:07

ENTSCHEIDUNG3M8

Verbesserung von UWUTM8 , die anders funktioniert

Versucht vorherzusagen, wann jemand beschleunigt und versucht, mehr Energie zu verbrauchen, um mehr Punkte zu sammeln

"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

Gewinner

Der Gewinner kümmert sich nicht um Ihre Regeln. Der Gewinner spielt nach seinen eigenen Regeln.

Der Gewinner versucht in so vielen Rennen wie möglich zu gewinnen (= auf Distanz 100 zu enden).

"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

Genau wie bei vielen Bots versuche ich, so schnell wie möglich fertig zu werden, indem ich so viel Energie wie möglich verwende. Es wird auch nicht versucht, an bestimmten Punkten überhaupt fertig zu werden, sondern es wird versucht, eine 9-Punkte-Marke zu erreichen

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