Garde tes distances!


15

Chaque joueur a un numéro. Le vôtre peut-il être le plus éloigné de tous?

Exigences

Écrivez une fonction Java, Python 2 ou Ruby nommée choose()qui accepte trois arguments:

  • un entier - le nombre de tours déjà terminés
  • un entier - le nombre de joueurs
  • un tableau de chaînes - les résultats de chaque tour précédent
    • chaque chaîne est une liste d'entiers séparés par des espaces, triés du plus bas au plus élevé

Par exemple, choose(2, 4, ["4 93 93 174", "1 84 234 555"])signifie:

  • il y avait déjà deux tours (c'est le troisième tour)
  • il y a un total de quatre joueurs
  • au premier tour, les nombres choisis étaient 4, 93, 93, 174
  • au deuxième tour, les nombres choisis étaient 1, 84, 234, 555

Vous devez renvoyer un nombre entier de 1 à 999 (inclus).

Pour chaque autre joueur, votre score est la racine carrée de la distance entre votre numéro et le leur. Votre score pour le tour est le total de tous ces scores.

100 tours seront joués. Le score total le plus élevé gagne!

Règles

  • Votre code ne doit pas utiliser d'E / S, y compris la console, les fichiers, le réseau, etc.
  • Vous ne pouvez pas interférer avec le programme de contrôle ou tout autre joueur.
  • Les programmes qui semblent enfreindre les règles ci-dessus seront exclus.
  • Chaque appel d'une fonction devrait prendre moins de cinq secondes sur mon ordinateur (Intel Core i5 2450M avec 8 Go de RAM).
  • Si un programme lève une exception ou renvoie une valeur non valide, il sera traité comme s'il renvoyait 1.
  • Chaque utilisateur peut soumettre au plus un programme.

Divers

  • Le programme de contrôle est sur GitHub .
  • Il y a trois joueurs intégrés. Ils peuvent être trouvés dans cette réponse .
  • Le gagnant sera choisi le 28 janvier.

Classement

Le gagnant est Conservateur .

Mention honorable à Gustav , le meilleur joueur avec une stratégie non constante.

  • Conservateur - 36226
  • Élevé - 36115
  • FloorHugger - 35880
  • NumberOne - 35791
  • Surestimateur - 35791
  • Gustav - 35484
  • Historien - 35201
  • Échantillonneur - 34960
  • Incrémenteur - 34351
  • JumpRightIn - 34074
  • Vickrey - 34020
  • Adolescent - 33907
  • Randu - 33891
  • Haltérophile - 33682
  • Intermédiaire - 33647
  • BounceInwards - 33529
  • NastyMathematician - 33292
  • Pull - 33244
  • Copycat - 33049

Les résultats complets peuvent être trouvés ici . (Je recommande de désactiver l'habillage du texte.)


Ai-je un moyen de savoir quel était mon propre numéro lors des tours précédents?
Martin Ender

@ MartinBüttner No.
Ypnypn

1
Je ne connais aucune de ces langues :( Pourriez-vous ajouter JavaScript? Comme, l'exécuter avec node.js?
Cilan

1
@TheWobbuffet, je n'en connais pas non plus. Cela ne m'a pas empêché de faire une entrée Python.
Mark

7
Je pense que cela aurait été plus intéressant si l'espace était un cercle / boucle de sorte que la distance entre 1 et 999 soit 1. Cela empêcherait le "deviner un nombre unique à chaque tour" de dominer, car il n'y a pas de "bords" se garer. Évidemment trop tard pour changer maintenant;)
Geobits

Réponses:


9

Python, conservateur

def choose(round, players, scores):
    return 999

Étant donné que chaque exception lance 1, il reste le plus loin possible d'elle. Fait fortune aux dépens des faibles.

Fait amusant: j'ai pensé à l'améliorer, mais je n'ai pas pu trouver de meilleur moyen que de me cacher dans un coin.


1
On dirait que j'ai choisi le mauvais coin. :(
TheNumberOne

C'est bon pour une raison simple: d'autres essaient de minimiser la distance jusqu'à vous. Ils amélioreront automatiquement votre score. Un changeur de jeu serait un adversaire qui essaye de se rapprocher le plus possible de vous.
Martin Thoma

1
Euh ... un point-virgule en Python?
KSFT

@KSFT hehe, je suis rouillé sur Python, et je n'ai jamais été cet expert de toute façon
clabacchio

6

Numéro un, Java

Le nom explique complètement celui-ci.

public static int choose(int round, int players, String[] args) {
    return 1;
}

1
Pourquoi le vote négatif?
TheNumberOne

5
J'aime particulièrement la façon dont le nom d'utilisateur est lié à la soumission
Brian J

5

Python, AncientHistorian

Est fermement convaincu que l'avenir sera exactement comme le passé, mais estime que le dernier cycle est trop récent pour être historique, il passe donc de 1 à 999 et choisit ce qui aurait été le meilleur des cycles précédents, à l'exception du dernier. Les 2 premiers tours retournent 500.

def choose(round, players, scores):
    calc = lambda n, scores: sum([abs(int(i)-n)**.5 for i in scores.split(' ')])
    return max(range(1, 1000), key=lambda n: sum([calc(n, j) for j in scores[1:]])) if round>1 else 500

4

Python, Vickrey

def choose(rounds, players, results):        
    if not results:
        return (id(0)/7)%999 + 1

    def best(array):
        score = lambda x: sum(abs(x-y)**.5 for y in array)
        m = max(score(x) for x in range(1, 1000))
        return [x for x in range(1, 1000) if score(x) == m]

    def second_best(array):
        array.extend(best(array))
        options = best(array)
        return options[(id(0)/7) % len(options)]

    results = [map(int, s.split()) for s in results]
    counts = {}

    for round_ in results:
        for number in round_:
            counts[number] = counts.get(number, 0) + 1

    most_common = sorted([(c, n) for n,c in counts.items()], reverse=True)
    to_avoid = [t[1] for t in most_common[:players]]

    return second_best(to_avoid)

Fait une liste de numéros qui ont été joués souvent, suppose que tout le monde jouera de manière optimale et opte pour le deuxième meilleur choix étant donné la liste.

Par exemple, si les nombres les plus courants sont [1, 990, 999], alors Vickrey insère le jeu optimal 200 à donner [1, 200, 990, 999], puis choisit la meilleure option pour le nouveau tableau (qui est 556).


4

Java, surestimateur

Comme son nom l'indique, ce programme suppose que tous les autres programmes essaieront de jouer "bien" en choisissant la meilleure réponse sur la base du dernier tour - donc ce "surestimateur" choisit toujours la pire position possible sur la base du tour précédent.

 public static int choose(int round, int players, String[] args) {
     String[] lastRoundStrings = args[args.length - 1].split(" ");
     int[] lastRound = new int[lastRoundStrings.length];
     int worstSelection = 0;
     for (int i = 0; i < lastRound.length; i++) {
         double worstScore = Double.MAX_VALUE;
         for (int j = 1; j < 999; j++) {
             double computedScore = score(j, lastRound);
             if (computedScore < worstScore) {
                 worstScore = computedScore;
                 worstSelection = j;
             }
         }
     }
     return worstSelection;
 }

 public static double score(int position, int[] otherPositions) {
     double total = 0;
     for (int i = 0; i < otherPositions.length; i++) {
         total += Math.sqrt(Math.abs(otherPositions[i] - position));
     }
     return total;
 }

Pourquoi cela a-t-il joué un "1" constant? Y avait-il un bug? Attention, jouer au "1" s'est avéré être un vrai succès. :)
Emil

Malheureusement, le code a un bug, oui - il n'analyse jamais réellement les scores qu'il lit du dernier tour. (Mais je m'en suis rendu compte trop tard et il me semblait mal de modifier une soumission d'ici là, et comme vous dites que ça se passait plutôt bien alors ... peu importe: p)
Alex Walker

4

Java - Haltérophile

Boucles de 1 à 999 pour déterminer laquelle serait la meilleure pour chaque tour. Les pondère en fonction de la récence (les derniers tours ont plus de poids) et renvoie sa meilleure estimation globale. Avec un peu de chance, si des motifs se forment plus tard, cela pourra se reprendre.

Edit: Maintenant avec + Inf% de récursion en plus! Ne pas pouvoir stocker / enregistrer / voir ce que vous avez choisi lors des tours précédents est un glisser. La prise en compte de vos propres contributions vous dérange lorsque vous essayez de comprendre ce que les autres vont faire. Alors, calculons-le! Cela va maintenant récuser pour comprendre ce qu'il a choisi au tour précédent et ignorer cela lors du calcul du prochain mouvement.

Notez qu'il ignore vraiment vraiment sa propre entrée du dernier tour, mais comme celui-ci est le plus élevé, il semble fonctionner correctement. Cela pourrait être corrigé avec un peu plus de travail, mais j'attendrai le classement pour voir s'il est nécessaire.

int choose(int rounds, int players, String[] hist){
    if(rounds < 1)
        return 1;

    int lastChoice = choose(rounds-1,players,java.util.Arrays.copyOf(hist, hist.length-1));

    int[][] history = new int[hist.length][players];
    for(int i=0;i<hist.length;i++){
        String[] tokens = hist[i].split(" ");
        boolean flag = false;
        for(int j=0;j<tokens.length;j++){
            history[i][j] = Integer.parseInt(tokens[j]);
            if(i==history.length-1 && history[i][j]==lastChoice && !flag){
                flag = true;
                history[i][j] = -1;
            }
        }
    }

    double best = 0;
    int guess = 1;
    for(int i=1;i<1000;i++){
        double score = 0;
        for(int j=0;j<history.length;j++){
            double weight = (double)(j+1)/history.length;
            for(int k=0;k<history[j].length;k++){
                if(history[j][k] > 0)
                    score += Math.sqrt(Math.abs(history[j][k]-i)) * weight;
            }
        }
        if(score > best){
            best = score;
            guess = i;
        }
    }
    return guess;
}

Remarque: Même au tour 100, il se termine en moins d'une seconde sur mon PC quelque peu lent. Si cela vous prend trop de temps pour une raison quelconque, faites-le moi savoir afin que je puisse limiter la récursivité.
Geobits du

C'est aussi très rapide sur ma machine.
Ypnypn

3

Ruby, Copycat

Retourne simplement le numéro remporté la dernière fois.

def choose r, p, hist
  last = hist.last.split.map &:to_i
  scores = last.map{|n| last.map{|m| (n-m).abs ** 0.5 }.inject :+ }
  last[scores.index scores.max]
end

1
Que revient-il pour le premier tour? Oh peu importe. Les exceptions reviendraient 1.
mbomb007

3

Ruby, JumpRightIn

def choose(round, players, args)
    return 500 if args.size == 0
    last_round = args[-1].split.map(&:to_i) + [1000]
    max_gap = 0
    last = 0
    move = 1
    last_round.each { |i|
        gap = i - last - 1
        if gap > max_gap
            max_gap = gap
            move = (i + last)/2
        end
        last = i
    }
    move
end

C'est probablement la stratégie la plus simple. Il trouve le plus grand écart lors du dernier tour et choisit le numéro au milieu de cet écart.


Que revient-il pour le premier tour? 1?
mbomb007

@ mbomb007 Oh, j'oublie toujours ces rondes initiales embêtantes. Merci, il en retourne maintenant 500.
Martin Ender

3

Gustav (Python 2)

Il s'agit d'une méta-stratégie assez simple, copiée sans vergogne à partir d'une de mes anciennes réponses dans un défi KotH similaire. Il prend en compte quelques stratégies simples, examine comment elles se seraient comportées au cours de tous les tours précédents, puis suit le score le plus élevé pour le tour suivant.

def choose(k, N, h):
    if k<2: return 999
    H = [[int(x) for x in l.split()] for l in h]
    score = lambda x,l: sum(abs(x-y)**.5 for y in l)
    S = [range(1,1000)
         + [max(range(1,1000), key=lambda x: score(x, H[i-1]))]
         + [max(range(1,1000), key=lambda x: score(x, H[i-2]))]
         + [min(range(1,1000), key=lambda x: score(x, H[i-1]))]
         + [min(range(1,1000), key=lambda x: score(x, H[i-2]))]
         for i in range(2,k+1)]
    scores = [sum(score(s[j],l) for s,l in zip(S[:-1], H[2:]))
              for j in range(len(S[0]))]
    return max(zip(scores, S[-1]))[1]

Je me rends compte maintenant que l'algorithme a encore quelques défauts. Par exemple, il pourrait continuer à "se poursuivre" parce qu'il ne distingue pas ses propres mouvements de ceux des adversaires. Cependant, je vais le laisser comme ça pour l'instant.



1

Les trois programmes suivants sont intégrés.

Élevé (rubis)

def choose(round, players, args)
    return 990
end

Incrémenteur (Java)

public static int choose(int round, int players, String[] args) {
    return round * 10 + 5;
}

FloorHugger (Python)

def choose(round, players, args):
    if len(args) == 0:
        return 10
    last = args[-1].split();

# next line from http://stackoverflow.com/a/7368801/3148067
    last = map(int, last)

    dist = 0
    for i in range(1, 999):
        if i in last:
            dist = 0
        else:
            dist = dist + 1
            if dist == 10:
                return i
    return 500

1

Python, Sampler

Dans une liste de lieux, choisissez celui qui est le plus éloigné des numéros récemment utilisés, en ignorant le tour précédent.

def choose(turn, players, history):
    sample = map(int, (' '.join( history[-5:-1] )).split())
    def distance(x): return sum(abs(x-y)**0.5 for y in sample)
    places = range(1, 1000, 13)
    score, place = max((distance(x), x) for x in places)
    return place

1

Java, BounceInwards

À partir de 1, il se rapproche progressivement de 500 tout en rebondissant entre l'option supérieure et inférieure.

public static int choose(int round, int players, String[] args) {
    return round%2 == 0 ? round * 5 : 1000 - round * 5;
}

1

NastyMathematician (Java)

Examine les deux derniers tours (si les meilleurs nombres étaient 70 et 80, il affichera 90). C'est méchant car il essaie de prendre le plus de numéros possible pour gagner contre ses adversaires.

public static int choose(int round, int players, String[] args) {
    if (round == 0) {
        return 999;
    }

    int[][] results = new int[args.length][players];

    // parse input
    for (int i = 0; i < args.length; i++) {
        String[] rounds = args[i].split(" ");
        for (int j = 0; j < rounds.length; j++) {
            results[i][j] = Integer.parseInt(rounds[j]);
        }
    }

    int bestNumber = 0;
    double bestScore = -1;

    // get the best number for the last round
    for (int i = 1; i < 1000; i++) {
        double score = 0;
        for (int result : results[results.length - 1]) {
            score += Math.sqrt(Math.abs(i - result));
        }
        if (score >= bestScore) {
            bestScore = score;
            bestNumber = i;
        }
    }

    if (round == 1) {
        return bestNumber;
    }

    int bestNumber2 = 0;
    double bestScore2 = -1;

    // get the best number for the second last round
    for (int i = 1; i < 1000; i++) {
        double score = 0;
        for (int result : results[results.length - 2]) {
            score += Math.sqrt(Math.abs(i - result));
        }
        if (score > bestScore2) {
            bestScore2 = score;
            bestNumber2 = i;
        }
    }

    // add the difference between last round and second last round to get this rounds best number
    int difference = bestNumber - bestNumber2;
    bestNumber = bestNumber + difference;

    return bestNumber > 999 ? 999 : bestNumber;
}

1

Python - Je ne veux pas penser à un nom ...

Si la moyenne des nombres choisis lors des tours précédents est inférieure à 500, il choisit 999. Il choisit 1 sinon.

def choose(a,b,c):
    total=0
    for i in c:
        for j in i.split(" "):
            total+=int(i)
    average=total/(a*b)
    if average<500:
        return 999
    return 1

0

Python, Middleman (d'après le restaurateur de @clabacchio)

def choose(round, players, scores):
    return 500;

Après avoir remarqué que le bord supérieur avait un score élevé (et surpassait le bord inférieur), je me demandais s'il pouvait y avoir quelque chose de pire que de se faire prendre au milieu.


0

Pull (rubis)

def choose(round, players, args)
    495*(round%3)+5
end

Alterne entre le bas, le milieu et le haut. (5 500 995)

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.