Simple Race KoTH

Aug 25 2020

Este desafio terminou oficialmente. Submissões posteriores não serão competitivas (mas ainda assim são bem-vindas!). Veja a pontuação aqui

Neste desafio, as submissões ("bots") devem ser funções Javascript que tentam ganhar o maior número de corridas possível. Em cada corrida, os bots terão uma quantidade limitada de energia que deve ser usada para viajar o mais rápido possível por uma distância de 100 unidades.

Mecânica

Cada jogo consistirá em uma série de corridas, que são compostas por uma série de turnos. Em cada turno, os bots escolherão uma distância não negativa para avançar. A quantidade de energia consumida é igual ao quadrado da distância percorrida (e os bots só viajarão até onde seu nível de energia permitir se eles retornarem por uma distância maior).

No início de cada corrida, as posições dos bots são redefinidas para 0. Quando um ou mais bots atingirem uma distância de 100 unidades, a corrida terminará. Observe que se um bot retornar uma distância que o colocaria a mais de 100 unidades desde o início, ele só se moverá o quanto for necessário para vencer. No início de todas as corridas, os bots receberão 100 de energia além do restante da última corrida. Eles também receberão um bônus de 1 energia para cada unidade que viajaram na corrida anterior.

Pontos

No final de cada corrida, todos os bots receberão um ponto para cada dez unidades que eles viajaram quando o jogo terminar (incluindo frações de um ponto). No final do jogo, o bot com mais pontos vence.

Como é provável que os bots sejam otimizados para um certo número de corridas, haverá 5 categorias nas quais todos os bots competirão: 250 corridas, 750 corridas, 2500 corridas, 7500 corridas e 25000 corridas. A pontuação geral de um bot será a soma de sua pontuação média por corrida em cada uma dessas categorias, tornando a pontuação mais alta possível 50.

Entrada / saída

Os bots receberão os seguintes argumentos: dist(a distância que percorreram na corrida atual), energy(a quantidade de energia que eles têm), bots(uma matriz das distâncias de todos os outros bots no final da última curva, embaralhada no final de cada corrida), e storage, que por padrão é um objeto vazio e pode ser usado para armazenar informações entre corridas.

Bot de exemplo

O seguidor tentará se manter à frente do bot médio, pela quantidade média movida por turno.

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

Controlador

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

Regras

  • Se nenhum bot se mover por três turnos consecutivos, a corrida terminará (as pontuações ainda serão contadas)

  • Se um bot errar, ele perderá uma vez (ou seja, não se moverá)

  • Bots não podem adulterar o controlador ou outros bots, ou ser mal-intencionados

  • Os bots devem ser executados em um período de tempo razoável

  • Os bots devem ser determinísticos; sem aleatoriedade a menos que seja semeado pelos argumentos do bot

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

Bots com vencimento em: sexta - feira, 4 de setembro, 12:00 UTC (08:00 EDT)

Respostas

3 Moogie Aug 31 2020 at 18:56

Compensador

Um derivado de "SubOptimal", o Compensator não fornece explicitamente a estratégia "Horda / Explosão", em vez disso, compensa naturalmente por meio do conhecimento de que se não foi o primeiro, pode não ter usado toda a sua energia no turno anterior e assim ele pode ter mais energia do que o esperado. Para capitalizar sobre esse suprimento excessivo de energia, este bot usará metade do excesso de energia para tentar forçar uma corrida mais rápida, mas mantendo a outra metade em reserva para tentar estender o efeito do excedente de energia em várias corridas.

Ele parece ter um desempenho ligeiramente melhor do que seu irmão (SubOptimal) e, conforme o tempo desta apresentação, está um pouco à frente de todos os outros 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

Controle de taxa

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

Cada rodada usa toda sua energia para chegar à linha de chegada. Estritamente melhor do que "Devagar e sempre", pois esta entrada usará apenas 1 ou mais energia por turno, ao mesmo tempo que garante sempre chegar ao fim. Inoptimizado, mas ainda bastante rápido.

4 Alion Aug 25 2020 at 20:19

Lento e constante

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

Baseline bot enquanto tento descobrir o que fazer com este desafio. Não se adapta de forma alguma, então pode começar a perder consistentemente se algum tipo de meta se desenvolver.

3 jonatjano Aug 26 2020 at 19:56

pré-computado

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

começa a corrida calculando o caminho completo até o fim, a fim de se mover quase a mesma velocidade durante toda a corrida, usando toda a energia disponível

3 Spitemaster Aug 27 2020 at 02:06

Noventa

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

Tem como objetivo obter 9 pontos por rodada. Não tenho certeza de como ele se sai, mas é menos provável que perca pontos para bots que terminam mais rápido do que ele (em comparação com o Rate Control, do qual este é derivado).

3 SomoKRoceS Aug 29 2020 at 03:34

Pulso

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

Cada etapa usa apenas 1% da energia. A cada 500 voltas, toma a distância do primeiro colocado neste momento e adiciona 50 ultrapassagens.

3 TheNumberOne Aug 30 2020 at 02:31

Jack in the Box

Economiza energia até vencer o jogo em 40 movimentos, diminuindo o número médio de movimentos por jogo.

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

Simplório

Simpleton só quer ganhar :(

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

Constante

Steady tenta fazer a mesma quantidade em cada turno, mas não gosta de ter energia extra.

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

Subótimo

A solução ideal onde não há meios de afetar outros pilotos é usar toda a sua energia para garantir que você termine em primeiro para ganhar o máximo de energia na próxima rodada e negar energia para seus oponentes. Para isso pode ser alcançado, gastando 1,0 energia por volta na primeira corrida e, em seguida, 2,0 por volta nas corridas subsequentes (devido à energia extra de 100 dada para vencer a distância de 100)

Isso pode ser feito calculando a energia do bot / a distância a percorrer no início de uma corrida, armazenando esse valor e retornando esse valor a cada volta da corrida.

Agora que sabemos a solução ideal sem os efeitos do oponente, precisamos considerar as ações que os oponentes podem realizar e que podem afetar os outros. Neste jogo, o único efeito real é a capacidade de forçar o fim da corrida atual sendo o vencedor. Uma vez que os bots podem acumular energia, eles podem decidir minimizar o consumo de energia e maximizar a produção de energia, sacrificando a chance de ganhar muitos pontos para qualquer corrida em particular e, em vez disso, gastando os pontos acumulados em uma corrida para dominar os outros bots e vencer aquela raça. Embora essa estratégia não dê pontos altos no geral, ela afeta os bots que esperam que as corridas terminem após 100 turnos. A contagem média de voltas de uma corrida é reduzida. Então, para compensar esse efeito, uma solução subótima é derivada da solução ótima adicionando um fator que emula o efeito de bots que usam essa estratégia de "explosão de tesouro".

Este fator não pode ser calculado a menos que o bot incorpore todas as outras estratégias de bot e, em seguida, execute uma análise para determinar o fator. Isso não está realmente no espírito dos desafios do KoTH e pode não ser permitido. Portanto, para este bot, uma análise empírica simples foi realizada para determinar o fator no momento da submissão e adicionar um escalar com base no número de submissões que aumentará o fator conforme mais submissões, supondo que bots posteriores possam interferir mais.

Em última análise, a fórmula é:

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

Este bot fará uma das três coisas em uma corrida:

  • Mova uma unidade por turno: Isso é feito na primeira corrida de cada jogo para garantir que tenha todos os 200 de energia de que precisa
  • Mova-se ligeiramente mais lento do que duas unidades por turno: Isso é feito a cada dois turnos e economiza energia suficiente para permitir que ...
  • Mova-se um pouco mais rápido do que duas unidades por volta: isso permite que ele termine uma curva mais rápido do que os concorrentes atuais e apenas prejudica alguns dos vencedores anteriores (embora o controle de taxa esteja um centésimo de ponto à frente na publicação)
2 RedwolfPrograms Aug 26 2020 at 01:37

Colecionador

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

O coletor se moverá por padrão a uma taxa de 0,5 unidades / volta. Isso é ideal para reunir energia. Se prevê no início de uma corrida que pode empatar ou bater a média com a energia que possui, então tentará fazê-lo.

Atualmente perde para o controle de Taxa, mas pode ser capaz de se adaptar melhor a novas estratégias.

2 Neil Aug 26 2020 at 06:37

Ganancioso / ganancioso

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

Greedy se moverá 2 unidades / turno se tiver mais de 100 de energia, caso contrário, 1 unidade / turno. O mais ganancioso moverá 2 unidades / turno se achar que provavelmente terá energia suficiente para cada um dos lados, caso contrário, 1 unidade / turno. Essas foram as maneiras mais simples que eu poderia pensar em usar qualquer energia de bônus que o bot pudesse ter.

2 ATaco Aug 27 2020 at 15:12

Sprinter Calculado

O Sprinter calculado tenta dar a volta completa o mais rápido possível com o combustível restante. Não é inteligente o suficiente para planejar corridas futuras, está apenas feliz por estar aqui para correr.

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

(Novo) Acelerar

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

Calma, estou apenas experimentando bots extremamente simples.

Este bot é muito fácil de entender. Ele inicialmente funciona na velocidade 0,001 e acelera quadraticamente.

2 null Aug 27 2020 at 17:56

Eu amo aleatoriedade

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

Surpresa / Tempo

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

Calcule uma velocidade fixa com base em quanto tempo os jogos geralmente duram. O tempo então tenta acertar o alvo, enquanto a surpresa tenta vencê-lo.

Durante a execução de testes com ambos, tornou-se aparente que provavelmente precisamos de regras sobre conluio neste KotH, embora as interações sejam mínimas. A surpresa pode fazer o Timing se sair muito melhor ao sacrificar sua própria pontuação para diminuir a duração da corrida, e pode ajudar ainda mais fazendo isso apenas em intervalos fixos que o Timing conhece.

Não estou fazendo essas travessuras agora porque presumo que não estejam no espírito.

1 RedwolfPrograms Aug 27 2020 at 20:37

Mímico

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

Cria uma lista de todos os movimentos (efetivos) de outros bots no último turno e seleciona um pseudo-aleatório usando uma versão melhor do PRNG de HighlyRadioactive. Ele garante que esses valores estejam dentro de um determinado intervalo (o que acontece na metade das vezes), então não faz nada estúpido.

1 null Aug 26 2020 at 17:18

Rápido e não estável

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

Mais rápido que lento

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

Se você acha que este é um bot ruim, então não.

Faster than Slow 48.951

1 alexberne Aug 29 2020 at 16:38

Todo

Whole não gosta de distâncias fracionárias e sempre se moverá em uma distância que seja um número inteiro.

    "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

Quarenta e nove

Fourty-Nine deu uma olhada em Winner & Winner2 e reconheceu que 49 rodadas para vencer é melhor do que 48 rodadas para vencer. Mas o Fourty-Nine quer vencer de acordo com suas regras. Portanto, Fourty-Nine não sacrifica sua distância média para ganhar muitas corridas. Mas nunca será mais rápido do que 49 voltas para vencer.

    "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

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

O Predictor assume que quanto mais bots são adicionados, mais rápido ele precisa para vencer. Ele coleta energia ao longo do tempo e, em seguida, corre em direção à linha de chegada de maneira semelhante a Colecionador ou Jack in the box.

1 NoOorZ24 Sep 04 2020 at 17:07

DECISION3M8

Melhoria no UWUTM8 que funciona de maneira diferente

Tenta prever quando alguém está acelerando e tenta usar mais energia para ganhar mais pontos

"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

Vencedora

O vencedor não se importa com suas regras. O vencedor joga de acordo com suas próprias regras.

O vencedor tenta vencer (= termina na distância 100) em tantas corridas quanto possível.

"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

Assim como muitos bots, eu tento terminar o mais rápido possível usando o máximo de energia possível. Ele também não tenta terminar em marcas específicas, mas tenta chegar à marca de 9 pontos

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