50
\$\begingroup\$

This challenge is based off of the game show, Weakest Link. For those unfamiliar with the show, the crux of this challenge deals with who you vote off:

  • If the other players are smarter than you, then you have less of a chance of getting the pot.
  • If the other players are dumber than you, then you have less of a pot to get.

At the start of each round, the Pot starts out with $0. A group of 9 players is formed, and each player is given a unique Smartness from 1 to 9.

At the start of each turn, Pot += Smartness for each player still in the round. Then, players vote on the player they wish to remove. The player with the most votes is removed. In case of a tie, the smarter player is kept.

When there are only 2 players are left in the round, they face off in a battle of wits. The chance of the player winning is Smartness/(Smartness+OpponentSmartness). The winning player then receives the entire pot.

The player who has received the most money at the end of the game wins.

Input / Output

Each turn, you will receive the current list of opponents. You will have access to your smartness and all players' entire voting history for the round via functions in the Player class.

As output, you must return a single integer, representing the player you wish to vote for (representing their smartness). Voting for yourself is allowed (but not recommended).

Rounds of 9 will repeat until all players have played at least 100010000 rounds, and all players have played in the same number of rounds.

You can find the controller here: https://github.com/nathanmerrill/WeakestLink

To create a player, you need to extend the Player class, and add your player to the PlayerFactory class. Your class must follow the following rules:

  1. Communication or interference with any other player (include your other players of the same type) is strictly prohibited.

  2. Reflection and static variables (except for constants) are not allowed.

  3. If you want to use randomness, I have provided a getRandom() function in the Player class. Use it, so simulations can be deterministic.

I have provided many functions in the Player class for easy access to data. You can find them online on Github. Your player will be instantiated each new round. "Dumb/suicidal" players are allowed (but not players with the same strategy).

Scores

377195  WeakestLink.Players.PrudentSniper
362413  WeakestLink.Players.Sniper
353082  WeakestLink.Players.VengefulSniper
347574  WeakestLink.Players.AntiExtremist
298006  WeakestLink.Players.BobPlayer
273867  WeakestLink.Players.MedianPlayer
247881  WeakestLink.Players.TheCult
240425  WeakestLink.Players.Leech
235480  WeakestLink.Players.SniperAide
223128  WeakestLink.Players.Guard
220760  WeakestLink.Players.Anarchist
216839  WeakestLink.Players.RevengePlayer
215099  WeakestLink.Players.IndependentVoter
213883  WeakestLink.Players.SniperKiller
210653  WeakestLink.Players.MaxPlayer
210262  WeakestLink.Players.Bandwagon
209956  WeakestLink.Players.MeanPlayer
208799  WeakestLink.Players.Coward
207686  WeakestLink.Players.Spy
204335  WeakestLink.Players.Hero
203957  WeakestLink.Players.MiddleMan
198535  WeakestLink.Players.MinPlayer
197589  WeakestLink.Players.FixatedPlayer
197478  WeakestLink.Players.HighOrLowNotSelf
181484  WeakestLink.Players.RandomPlayer
165160  WeakestLink.Players.BridgeBurner
\$\endgroup\$
  • 1
    \$\begingroup\$ don't get this: "At the start of each round... A group of 9 players is formed, and each player is given a unique Smartness" not at the start of the game start? \$\endgroup\$ – CSᵠ Dec 1 '15 at 5:41
  • 1
    \$\begingroup\$ @CSᵠ correct. Your smartness changes from round to round (it would be unfair otherwise). \$\endgroup\$ – Nathan Merrill Dec 1 '15 at 5:45
  • 2
    \$\begingroup\$ must be the stress and joy \$\endgroup\$ – CSᵠ Dec 1 '15 at 5:56
  • 1
    \$\begingroup\$ Should I expect an ant build config or some such? I'm kinda new to java, and I'm not sure how people usually set up little projects like this one. \$\endgroup\$ – Dale Johnson Dec 1 '15 at 7:12
  • 4
    \$\begingroup\$ From within src\WeakestLink I used javac Game\*.java Players\*.java Main.java to compile and java -cp .. WeakestLink.Main to run. \$\endgroup\$ – Linus Dec 1 '15 at 7:22

28 Answers 28

22
\$\begingroup\$

Sniper

The general idea is to keep around one of the stupid players (i.e. ones we are more likely to beat in the face-off) to snipe for the points. After that we try to remove the other low-value players to raise the pot. But when we get to the intelligent players we opt to remove for the most dangerous ones in case our stupid player gets removed. That way if we don't have someone to snipe we should get someone we at least stand a chance against. Also, since we always vote with either a min or max player, I expect we are fairly effective in getting our way.

package WeakestLink.Players;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;
public class Sniper extends Player {
    @Override
    public int vote(Set<Integer> currentOpponents) {
        int smrt = getSmartness();

        //count number of players smarter/stupider than me
        Iterator<Integer> opps = currentOpponents.iterator();
        int cnt_smrt=0, cnt_stpd=0, opp_smrt, min_stpd=10, max_smrt=0;

        while(opps.hasNext()){
            opp_smrt = opps.next().intValue();
            if(opp_smrt > smrt){
                cnt_smrt++;
                if(opp_smrt > max_smrt) max_smrt = opp_smrt;
            }
            else if(opp_smrt < smrt){
                cnt_stpd++;
                if(opp_smrt < min_stpd) min_stpd = opp_smrt;
            }
        }

        //remove low-value, then dangerous players
        if(cnt_stpd>1)
            return min_stpd;
        else
            return max_smrt;
    }
}
\$\endgroup\$
  • \$\begingroup\$ So apparently rollback doesn't remove edits. If at all possible I'd like any edits removed to make it abundantly clear this is the original version. \$\endgroup\$ – Linus Dec 2 '15 at 20:38
12
\$\begingroup\$

PrudentSniper

Sniper, but with two special-case behaviors. One is that if there are three bots left, and PrudentSniper is the smartest, it will vote for the middle bot instead of the least smart. This allows it to win a few more showdowns. The other behavior is that if the smartest bot is gunning for it (voted for it or the analogous bot last time) and the least smart isn't, it will vote for the smartest in self defense.

package WeakestLink.Players;
import WeakestLink.Game.Vote;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;
public class PrudentSniper extends Player {
    @Override
    public int vote(Set<Integer> currentOpponents) {
        int smrt = getSmartness();

        //count number of players smarter/stupider than me, find max/min
        Iterator<Integer> opps = currentOpponents.iterator();
        int cnt_smrt=0, cnt_stpd=0, opp_smrt, min_stpd=10, max_smrt=0;

        while(opps.hasNext()){
            opp_smrt = opps.next().intValue();
            if(opp_smrt > max_smrt) max_smrt = opp_smrt;
            if(opp_smrt < min_stpd) min_stpd = opp_smrt;
            if(opp_smrt > smrt){
                cnt_smrt++;
            }
            else if(opp_smrt < smrt){
                cnt_stpd++;
            }
        }

        //identify enemies
        Iterator<Vote> votes = getRecentVotes().iterator();
        boolean[] voted_for_me = new boolean[9];

        while(votes.hasNext()) {
          Vote opp_vote = votes.next();
          voted_for_me[opp_vote.getVoter()] = (opp_vote.getVoted() == getSmartness() ||
                                              (opp_vote.getVoted() < getSmartness() && cnt_stpd < 1) ||
                                              (opp_vote.getVoted() > getSmartness() && cnt_smrt < 1));
        }

        if (currentOpponents.size() < 3 || cnt_stpd < 2 || (voted_for_me[max_smrt] && !voted_for_me[min_stpd] && cnt_smrt > 0) )
          return max_smrt;
        else
          return min_stpd;
    }
}
\$\endgroup\$
  • \$\begingroup\$ I'm not sure that both advancements are of equal worth. Have you tried them each separately? \$\endgroup\$ – Linus Dec 2 '15 at 22:28
  • \$\begingroup\$ I did, and while the three bots case is an unambiguous improvement (at least against the bots currently in master on the repo), the retribution is about neutral, on net. I left it in as a sort of vague deterrent, a precaution against assassins. \$\endgroup\$ – histocrat Dec 2 '15 at 22:57
  • \$\begingroup\$ relevant ;) \$\endgroup\$ – Conor O'Brien Dec 3 '15 at 18:44
  • \$\begingroup\$ I recently fixed a bug where smartness was 0-8 instead of 1-9. This broke your code, so I fixed it (you can find the updated code on the repository): github.com/nathanmerrill/WeakestLink/blob/master/src/… \$\endgroup\$ – Nathan Merrill Dec 6 '15 at 14:11
12
\$\begingroup\$

TheCult

The cult players have a mildly esoteric voting scheme by which they attempt to identify each other and vote as a group, using only the voting record. Since each member of the cult knows how to vote, anyone who votes differently is revealed as a non-member and eventually targeted for elimination.

The voting scheme at a glance:

  • on the first turn vote for weakest contestant, working in concert with min & sniper helps the cult gain power
  • on subsequent turns vote off known non-members until only the cult remains (we vote off the lowest-value non-member to rack up points as long as we think we are in control).
  • when only members remain, vote off low-value members for points (indeed sacrificing yourself for the good of the cult).

The Code:

package WeakestLink.Players;
import WeakestLink.Game.Vote;
import java.util.Iterator;
import java.util.Set;
public class TheCult extends Player {
    private int cult_vote;
    private boolean[] isMember = null;
    @Override
    public int vote(Set<Integer> currentOpponents) {
        //on first turn, vote the code
        if(isMember == null){
            isMember = new boolean[10];
            for(int i=10; --i!=0;) isMember[i]=true; //runs 9-1
            return cult_vote = 1;
        }
        //on all other turn, assess who is not voting with the cult
        Vote opp_vote;
        int cult_cnt=0;
        Iterator<Vote> votes = getRecentVotes().iterator();
        while(votes.hasNext()){
            opp_vote = votes.next();
            if(opp_vote.getVoted() != cult_vote)
                isMember[opp_vote.getVoter()] = false;
            else
                cult_cnt++;
        }
        //find weakest and stongest non-members, and weakest members
        Iterator<Integer> opps = currentOpponents.iterator();
        int opp_smrt, min_mem=10, min_non=10, max_non=0;
        while(opps.hasNext()){
            opp_smrt = opps.next().intValue();
            if(isMember[opp_smrt]){
                if(opp_smrt < min_mem) min_mem = opp_smrt;
            }else{
                if(opp_smrt < min_non) min_non = opp_smrt;
                if(opp_smrt > max_non) max_non = opp_smrt;
            }
        }
        if(cult_cnt>2 && min_non!=10) cult_vote = min_non;
        else if(max_non!=0)           cult_vote = max_non;
        else                          cult_vote = min_mem;
        return cult_vote;
    }
}

Final Thoughts:

The cult now switch's to voting for the most dangerous players when there are only two or fewer cult-members remaining for the face-off. I tested it several times with cult_cnt>1 and cult_cnt>2 conditions and the later wins more often.

Still, this is a precaution and the cult really isn't designed to work as lone player, so as the number of new players increase the cult should still lose eventually.

\$\endgroup\$
  • \$\begingroup\$ Would this not be better to vote off the smartest non-members first? \$\endgroup\$ – agweber Dec 1 '15 at 20:24
  • \$\begingroup\$ I have updated the controller code so that the random variable is static (and can be accessed via Game.random). I've also taken the liberty of updating the code on github: github.com/nathanmerrill/WeakestLink/blob/master/src/… \$\endgroup\$ – Nathan Merrill Dec 1 '15 at 20:33
  • 1
    \$\begingroup\$ @NathanMerrill Thanks, but I'm seem to get better results if I allow the cult to be more tolerant (go figure) to unknown non-members who vote in it's interest. \$\endgroup\$ – Linus Dec 1 '15 at 20:57
  • \$\begingroup\$ @agweber, Thanks for the suggestion. This probably makes thecult a better sole-player, but as long as it has the numbers it should try to run up the pot. I think my new version is the best of both world. \$\endgroup\$ – Linus Dec 1 '15 at 20:59
  • 2
    \$\begingroup\$ TheCult bid me to request that unusedPlayers.addAll(allPlayers); in Game.java be duplicated around nine times, so that all players can occur in varied multiplicity (like shuffling together multiple decks of cards)... no don't of course, that's totally a biased request but it's interesting to see just how powerful the team based strategy can still be if they have even a small chance of getting put together. \$\endgroup\$ – Linus Dec 2 '15 at 7:52
7
\$\begingroup\$

BridgeBurner

Not somewhere where I can test this right now, and this came out feeling like really ugly/dumb code, but it should work.

This bot just wants to be hated. It votes for whoever has voted against it the least. In the event of a tie, it picks whoever has gone the longest without voting for it. In the event of another tie, it picks the smartest of those (presumably because they will make the worst enemy). It will not vote for itself, because no one will really hate it when it's not around.

package WeakestLink.Players;

import WeakestLink.Game.Vote;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class BridgeBurner extends Player{
    @Override
    public int vote(Set<Integer> currentOpponents) {
        List<Integer> votes_against = Stream.generate(() -> 0).limit(9).collect(Collectors.toList());
        List<Integer> last_voted_against = Stream.generate(() -> 0).limit(9).collect(Collectors.toList());
        Iterator<Vote> votes_against_me = getVotesForSelf().iterator();

        for (int c = 0; c < 9; c++){
            if (!currentOpponents.contains(c)){
                votes_against.set(c,-1);
                last_voted_against.set(c,-1);
            }
        }

        while(votes_against_me.hasNext()){
            Vote vote = votes_against_me.next();

            int voter = vote.getVoter();
            int round = vote.getRound();

            if (currentOpponents.contains(voter)){
                votes_against.set(voter, votes_against.get(voter)+1);
                last_voted_against.set(voter, Math.max(round, last_voted_against.get(voter)));
            } else {
                votes_against.set(voter, -1);
                last_voted_against.set(voter, -1);
            }
        }

        int min_tally = Collections.max(votes_against);
        for (int c = 0; c < 9; c++){
            int current_tally = votes_against.get(c);
            if (current_tally != -1 && current_tally < min_tally){
                min_tally = current_tally;
            }
        }

        if (Collections.frequency(votes_against, min_tally) == 1){
            return votes_against.indexOf(min_tally);
        } else {
            List<Integer> temp_last_against = new ArrayList<>();
            for (int c = 0; c < 9; c++){
                if (votes_against.get(c) == min_tally){
                    temp_last_against.add(last_voted_against.get(c));
                }
            }
            return last_voted_against.lastIndexOf(Collections.min(temp_last_against));
        }
    }
}
\$\endgroup\$
  • \$\begingroup\$ I was unable to get this bot to work. I fixed several bugs, and it is now voting for a non-existent player. The one change I wasn't sure if it was right was that "last_round_voted" isn't defined, so I changed it to "last_voted_against". You can find my changes here: github.com/nathanmerrill/WeakestLink/blob/master/src/… \$\endgroup\$ – Nathan Merrill Dec 3 '15 at 3:00
  • \$\begingroup\$ @NathanMerrill This code was apparently even worse than I thought. Now that I can test it, I'll look at both versions and try to get this working. \$\endgroup\$ – SnoringFrog Dec 3 '15 at 15:02
  • \$\begingroup\$ @NathanMerrill Found a couple issues. Namely, I didn't ignore players who were not in the found that never voted for the bot, which is why it always tried to vote for them. Also used the wrong list to get an index from at one point, resulting in player -1 being voted for. But it should be fixed now. \$\endgroup\$ – SnoringFrog Dec 3 '15 at 18:32
  • 1
    \$\begingroup\$ Well, it works, but does terrible. Congrats on beating random player! \$\endgroup\$ – Nathan Merrill Dec 3 '15 at 18:42
  • 1
    \$\begingroup\$ @NathanMerrill on beating random player sometimes \$\endgroup\$ – SnoringFrog Dec 3 '15 at 21:49
6
\$\begingroup\$

Bandwagon

Follows the crowd in voting, unless he's the one being targeted.

package WeakestLink.Players;

import WeakestLink.Game.Vote;
import java.util.Map;
import java.util.Set;

/**
 * Votes for the currently most voted bot in the game. Or the lowest one.
 */
public class Bandwagon
        extends Player {

    @Override
    public int vote(Set<Integer> currentOpponents) {
        int self = getSmartness(), vote = -1;
        java.util.Map<Integer, Integer> votes = new java.util.TreeMap<>();
        getVotingHistory().stream().map((Vote v)-> v.getVoted()).filter((Integer i)-> !i.equals(self)).forEach((Integer tgt)-> {
            if(!votes.containsKey(tgt)) {
                votes.put(tgt, 1);
            } else {
                votes.put(tgt, votes.get(tgt) + 1);
            }
        });

        do {
            if(votes.entrySet().isEmpty()) {
                vote = currentOpponents.stream().filter((Integer i)-> !i.equals(self)).sorted().findFirst().get();
            } else {
                if(votes.containsKey(vote)) {
                    votes.remove(vote);
                    vote = -1;
                }

                for(Map.Entry<Integer, Integer> vv: votes.entrySet()) {
                    Integer key = vv.getKey();
                    Integer value = vv.getValue();

                    if((vote == -1) || (value > votes.get(vote))) {
                        vote = key;
                    }
                }
            }
        } while(!currentOpponents.contains(vote));

        return vote;
    }
}

I'm guessing this one will just make snipers stronger by following them, but also avoids being targeted by the cult and sniper-aides in a slightly effective manner. It may as well be a meatshield for sniper killers or aid them if there's more of them. (Need to test with the latest updates).

Using java 8 features because the game needs that to run anyway.

\$\endgroup\$
  • 1
    \$\begingroup\$ It's nice to see some well written code :) \$\endgroup\$ – Nathan Merrill Dec 3 '15 at 14:33
6
\$\begingroup\$

RevengePlayer

This bot will vote for whoever voted for him the most times, tiebreaker being the smartest player. The theory is that a player that voted for you in the past will likely vote for you again.

package WeakestLink.Players;
import java.util.Collections;
import java.util.Set;
import java.util.Iterator;
import WeakestLink.Game.Vote;
public class RevengePlayer extends Player{

    @Override
    public int vote(Set<Integer> opponents) {
        int[] A;
        A = new int[10];
        for(int i = 1;i < 10;i++)
            A[i] = opponents.contains(i)? i+1 : 0;
        Set<Vote> H = getVotingHistory();
        Iterator<Vote> I = H.iterator();
        while(I.hasNext()){
            Vote v = I.next();
            if(v.getVoted() == getSmartness())
                A[v.getVoter()] += A[v.getVoter()] != 0?10:0;
        }
        int maxI = 0;
        for(int i = 1;i < 10;i++)
            if(A[i] > A[maxI])
                maxI = i;
        return maxI;
    }
}
\$\endgroup\$
  • \$\begingroup\$ I recently fixed a bug where smartness was 0-8 instead of 1-9. This broke your code, so I fixed it (you can find the updated code on the repository): github.com/nathanmerrill/WeakestLink/blob/master/src/… \$\endgroup\$ – Nathan Merrill Dec 6 '15 at 14:10
  • \$\begingroup\$ @NathanMerrill your fix for my code had a small bug. I edited my code to make it better. \$\endgroup\$ – MegaTom Dec 9 '15 at 15:46
5
\$\begingroup\$

MeanPlayer

Vote neither the stupidest nor the smartest players, and he's carrying a gun (sneaked it past security)

public class MeanPlayer extends Player{

    @Override
    public int vote(Set<Integer> currentOpponents) {
        int mid = currentOpponents.size() / 2;
        Object[] sortedOpponents = currentOpponents.toArray();
        Arrays.sort(sortedOpponents);
        return (int) sortedOpponents[mid];
    }
}
\$\endgroup\$
  • \$\begingroup\$ I don't understand why this player is any meaner than the rest /sarc \$\endgroup\$ – Nathan Merrill Dec 1 '15 at 6:03
  • \$\begingroup\$ Watch out @NathanMerrill, he's got a gun! I'd choose my words carefully if I were you... \$\endgroup\$ – CSᵠ Dec 1 '15 at 6:11
  • 10
    \$\begingroup\$ @CSᵠ I'm not worried. When he's the average player, he uses the gun on himself. \$\endgroup\$ – quintopia Dec 1 '15 at 7:42
  • 14
    \$\begingroup\$ This player is less mean than she could be. She appears to be more median than mean. \$\endgroup\$ – Yakk Dec 1 '15 at 15:11
  • 7
    \$\begingroup\$ Hah.... took me a minute \$\endgroup\$ – David says Reinstate Monica Dec 1 '15 at 15:33
5
\$\begingroup\$

AntiExtremist

This extreme socialist believes that all people should be of equal smartness. He tries to kill those who are much smarter or dumber than he is. He considers both but he favors dumb in general. He favors dumb people in the beginning and smart in the end, but it's weighted based on how extreme those people are.

package WeakestLink.Players;
import java.util.Arrays;
import java.util.Set;

public class AntiExtremist extends Player {

    Object[] currentPlayers;

    @Override
    public int vote(Set<Integer> currentOpponents) {

        currentPlayers = (Object[]) currentOpponents.toArray();
        Arrays.sort(currentPlayers);

        int smartness = getSmartness();
        int turns = getTurnNumber();

        //// Lets get an idea of who's smart and who's dumb ////

        int smarter = 0, dumber = 0;

        int max_smart = 0, min_smart = 10;

        currentOpponents.toArray();

        for (int i = 0; i < currentPlayers.length; i++) {
            int osmart = (int)currentPlayers[i];

            if (osmart == smartness)
                continue;

            if (osmart > smartness) {
                smarter++;

                if (osmart > max_smart)
                    max_smart = osmart;
            }
            else if (osmart < smartness) {
                dumber++;

                if (osmart < min_smart)
                    min_smart = osmart;
            }

        }

        // int total = smarter+dumber;

        double smarter_ratio = smarter > 0 ? (max_smart-smartness)/4.5 : 0; 
        double dumber_ratio = dumber > 0 ? (smartness-min_smart)/3.0 : 0;//Favor dumber

        smarter_ratio*=.25+(turns/9.0*.75);
        dumber_ratio*=1-(turns/8.0*.75);

        return smarter_ratio > dumber_ratio ? max_smart : min_smart;

    }

}

NOTE: According to Linus this will vote the same as sniper the vast majority of the time (525602:1228).

\$\endgroup\$
  • \$\begingroup\$ I'm going to keep the current run to 10K for now (for quicker testing). When I do the final run through, I'll likely make it larger. \$\endgroup\$ – Nathan Merrill Dec 2 '15 at 1:42
  • \$\begingroup\$ I'm not trying to accuse you of anything, but this votes the way Sniper would ~99.7% of the time, it's basically going to be a coin flip as to who wins, since they are so close to the same strategy. \$\endgroup\$ – Linus Dec 2 '15 at 6:40
  • \$\begingroup\$ Where did you get that statistic from? I admit it has a semi-similar strategy but my goal was to try at improve upon something as simple as yours by choosing to vote for people whom are very smart if they are significantly smarter than me(aka I'm not likely to win the pot if they survive) \$\endgroup\$ – csga5000 Dec 2 '15 at 20:39
  • 1
    \$\begingroup\$ I gave your class a static Sniper S = new Sniper() and static long agrees=0, disagrees=0;. In your voting method I add S.setSmartness(getSmartness()); int sniper_answer=S.vote(currentOpponents); which calculates how a sniper would vote in your position, then put your answer into a variable to counted whether it agreed or disagreed before returning its answer. Once the game is over you can print agrees:disagress which was 525602:1228. \$\endgroup\$ – Linus Dec 2 '15 at 20:52
  • 1
    \$\begingroup\$ @Linus That makes sense, sounds legit. I'll add a note about that. \$\endgroup\$ – csga5000 Dec 2 '15 at 21:06
5
\$\begingroup\$

Spy

Spy is reserved. He doesn't like to gun for the smartest people. Likewise, he doesn't like picking on quartata defenseless idiots. So, he likes to eliminate those closest to him in smartness.

package WeakestLink.Players;

import java.util.Iterator;
import java.util.Set;

public class Spy extends Player{
  @Override
  public int vote(Set<Integer> currentOpponents) {
    int selfIntel = getSmartness();
    int closestIntel = 100; // default
    // get closest player
    Iterator<Integer> enemies = currentOpponents.iterator();
    while(enemies.hasNext()){
      int enemyIntel = enemies.next().intValue();
      if(Math.abs(enemyIntel - selfIntel) < closestIntel) closestIntel = enemyIntel;
    }
    return closestIntel;
  }
}

You've just been backstabbed, mes amis. He doesn't care if he wins. He just like the sound of the knife in your back as he votes you off successfully.

You've just been backstabbed.

\$\endgroup\$
  • 4
    \$\begingroup\$ That image, though. +1 \$\endgroup\$ – Addison Crump Dec 3 '15 at 20:12
  • \$\begingroup\$ I think this has a bug. Math.abs(enemyIntel - selfIntel) < closestIntel should be Math.abs(enemyIntel - selfIntel) < Math.abs(closestIntel - selfIntel). \$\endgroup\$ – MegaTom Dec 9 '15 at 17:46
  • \$\begingroup\$ @MegaTom I think you're right. I will check on this further when I have Java available. Thanks for the possible catch! \$\endgroup\$ – Conor O'Brien Dec 9 '15 at 19:00
4
\$\begingroup\$

MedianPlayer

This player attempt to be the meanest (well, medianness) one left.

It votes to eliminate the smartest and dumbest opponents (with a slight bias towards voting off the smartest), depending on if there are more or less smarter/dumber than themself.

package WeakestLink.Players;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;
public class MedianPlayer extends Player {
  @Override
  public int vote(Set<Integer> currentOpponents) {
    int smrt = getSmartness();

    //count number of players smarter/stupider than me
    Iterator<Integer> opps = currentOpponents.iterator();
    int cnt_smrt=0, cnt_stpd=0, min_stpd=10, max_smrt=0;

    while(opps.hasNext()){
      int opp_smrt = opps.next().intValue();
      if(opp_smrt > smrt){
        cnt_smrt++;
        if(opp_smrt > max_smrt)
          max_smrt = opp_smrt;
      } else if(opp_smrt < smrt){
        cnt_stpd++;
        if(opp_smrt < min_stpd)
          min_stpd = opp_smrt;
      }
    }

    // the middle must hold
    if(cnt_stpd>cnt_smrt)
      return min_stpd;
    else
      return max_smrt;
  }
}

framework blatantly stolen from @Linus above.

\$\endgroup\$
  • \$\begingroup\$ You made my IDE complain about duplicated code! \$\endgroup\$ – Nathan Merrill Dec 1 '15 at 19:45
  • \$\begingroup\$ @NathanMerrill Copy-pasta attack! Note I changed the name of the class since I posted. As I suppose duplicating someone else's class name to ensure that you cannot go against them would be against the spirit of the rules. \$\endgroup\$ – Yakk Dec 1 '15 at 19:50
  • 2
    \$\begingroup\$ Thanks for blatantly stealing my work, or admitting so at least. \$\endgroup\$ – Linus Dec 1 '15 at 19:52
  • 2
    \$\begingroup\$ @Linus You are welcome! Imitation is the best flattery, I hope. \$\endgroup\$ – Yakk Dec 1 '15 at 20:18
  • 2
    \$\begingroup\$ @csga5000 blatantly stole was his joke, and i was just playing along. Any half-competent coder (e.g. myself) would write the loop the same way, so all he really did was steal my variable names. If i had thought to copyright them maybe I could charge royalties ; ) \$\endgroup\$ – Linus Dec 3 '15 at 9:13
4
\$\begingroup\$

Coward

package WeakestLink.Players;
import WeakestLink.Game.Vote;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;
public class Coward extends Player {
  @Override
  public int vote(Set<Integer> currentOpponents) {

    boolean[] currentOpponent = new boolean[10];

    Iterator<Integer> opps = currentOpponents.iterator();
    while(opps.hasNext()){
      currentOpponent[opps.next().intValue()] = true;
    }

    int[] voteCounts = new int[9];
    for(int i=0; i<9; i++) {
        voteCounts[i] = 0;
    }

    Iterator<Vote> votes = getRecentVotes().iterator();

    while(votes.hasNext()){
      Vote opp_vote = votes.next();
      if(currentOpponent[opp_vote.getVoter()])
        voteCounts[opp_vote.getVoted()] += 1;
      else
        voteCounts[opp_vote.getVoter()] += 100;
    }

    int previous_weakest = -1;
    int max_votes_gotten = 0;
    for(int i=0;i<9;i++){
      if (voteCounts[i] > max_votes_gotten) {
        max_votes_gotten = voteCounts[i];
        previous_weakest = i;
      }
    }
    int min_closeness = 10;
    int to_vote = -1;
    int opp;
    int closeness;
    opps = currentOpponents.iterator();
    while(opps.hasNext()){
      opp = opps.next();
      closeness = Math.abs(opp - previous_weakest);
      if(closeness <= min_closeness) {
        to_vote = opp;
        min_closeness = closeness;
      }
    }

    return to_vote;

  }
}

Just doesn't want to get voted off, so votes for the opponent most similar to the player that got voted off last round in order to maximize the chance of being on the winning team.

Doesn't do particularly well right now, but might as well throw it in the mix.

\$\endgroup\$
4
\$\begingroup\$

Hero

Votes off those who pick on the weak ... or annoy him.

package WeakestLink.Players;

import WeakestLink.Game.Game;
import WeakestLink.Game.Vote;

import java.util.*;

/**
 * Created by thenumberone on 12/2/15.
 * @author thenumberone
 */
public class Hero extends Player{

    @Override
    public int vote(Set<Integer> currentOpponents) {
        int me = getSmartness();
        Set<Vote> history = getVotingHistory();
        history.removeIf(vote -> !currentOpponents.contains(vote.getVoter()) || vote.getVoter() == me);
        int[] evilnessLevel = new int[Game.NUMBER_PLAYERS_PER_ROUND];
        for (Vote vote : history){
            evilnessLevel[vote.getVoter()] += vote.getVoted() == me ? 1_000_000 : Game.NUMBER_PLAYERS_PER_ROUND - vote.getVoted();
        }
        int mostEvilOpponent = -1;
        for (int opponent : currentOpponents){
            if (mostEvilOpponent == -1 || evilnessLevel[opponent] > evilnessLevel[mostEvilOpponent]){
                mostEvilOpponent = opponent;
            }
        }
        return mostEvilOpponent;
    }
}
\$\endgroup\$
4
\$\begingroup\$

Bob

Bob is just the average guy that thinks he is smarter then he really is. Cannot win the sniper family but get top 5 in my simulations most of the time.

package WeakestLink.Players;

import java.util.Collections;
import java.util.Set;

import WeakestLink.Game.Vote;

public class BobPlayer extends Player {


    @Override
    public int vote(Set<Integer> currentOpponents) {
        int smartness;

        // Bob sometimes thinks he is smarter than he really is
        if (getRandom().nextInt(10) == 0) {
            smartness = 10;
        } else {
            smartness = getSmartness();
        }

        // If there is still some competition
        if (currentOpponents.size() > 3) {
            // And Bob is the dumbest
            if (smartness < Collections.min(currentOpponents)) {
                // Go for the smartest one
                return Collections.max(currentOpponents);
                // But if he is the smartest
            } else if (smartness > Collections.max(currentOpponents)) {
                // Go for the weak link
                return Collections.min(currentOpponents);
            } else {
                // Else revenge!
                for (Vote v : getRecentVotes()) {
                    if (v.getVoted() == smartness && currentOpponents.contains(v.getVoter())) {
                        return v.getVoter();
                    }
                }
            }
            return Collections.min(currentOpponents);
        } else {
            //If there are few opponents just revenge!
            for (Vote v : getRecentVotes()) {
                if (v.getVoted() == smartness && currentOpponents.contains(v.getVoter())) {
                    return v.getVoter();
                }
            }
            return Collections.max(currentOpponents);
        }
    }



}
\$\endgroup\$
4
\$\begingroup\$

FixatedPlayer

Picks a random target, then votes for them until they're gone. Won't vote for himself though.

package WeakestLink.Players;

import WeakestLink.Game.Vote;

import java.util.*;

public class FixatedPlayer extends Player{
    @Override
    public int vote(Set<Integer> currentOpponents) {
        int self = getSmartness();
        Vote previous_vote = getLastVote();
        if (previous_vote == null || !currentOpponents.contains(previous_vote.getVoted())){
            return (int) currentOpponents.toArray()[getRandom().nextInt(currentOpponents.size())];
        }
        else {
            return previous_vote.getVoted();
        }
    }
}
\$\endgroup\$
  • \$\begingroup\$ This code didn't work either, but was an easy fix. Your do-while is actually not necessary, because I don't give you your smartness when I pass you your currentOpponents. The fixed code can be found here: github.com/nathanmerrill/WeakestLink/blob/master/src/… \$\endgroup\$ – Nathan Merrill Dec 3 '15 at 3:01
  • \$\begingroup\$ @NathanMerrill I edited the corrected code into my answer so anyone looking at it here see's what's actually being run \$\endgroup\$ – SnoringFrog Dec 3 '15 at 18:37
4
\$\begingroup\$

Statistics

This is not an entry to the contest. This is merely a way to get useful statistics of a game. These statistics print off the percentage likelihood that a particular player will be voted off in a round.

To do this add the following lines to Round.java so that the top of the file looks like this:

package WeakestLink.Game;

import WeakestLink.Players.Player;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Round {

    private static int[][] statistics = new int[Game.NUMBER_PLAYERS_PER_ROUND - 2][Game.NUMBER_PLAYERS_PER_ROUND + 1];
    private static int[] counts = new int[Game.NUMBER_PLAYERS_PER_ROUND - 2];

    static {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            for (int i = 0; i < Game.NUMBER_PLAYERS_PER_ROUND - 2; i++){
                System.out.println();
                System.out.println("For " + (i+1) + "th round:");
                for (int j = 1; j <= Game.NUMBER_PLAYERS_PER_ROUND; j++){
                    System.out.println(String.format("%f%% voted for %d", 100.0*statistics[i][j]/counts[i], j));
                }
            }
        }));
    }

...

Then modify the vote method to look like this:

private Vote vote(Player player){
    player.setVotingHistory(new HashSet<>(votes));
    player.setTurnNumber(currentTurn);
    player.setPot(pot);
    Set<Integer> players = currentPlayers.stream()
            .filter(p -> p != player)
            .map(playerToSmartness::get)
            .collect(Collectors.toSet());
    int vote = player.vote(players);
    if (!currentPlayers.contains(smartnessToPlayer.get(vote))){
        throw new RuntimeException(player.getClass().getSimpleName()+" voted off non-existent player");
    }
    Vote v = new Vote(playerToSmartness.get(player), vote, currentTurn);
    counts[v.getRound()]++;
    statistics[v.getRound()][v.getVoted()]++;
    return v;
}

Example Output:

For 1th round:
55.554756% voted for 1
4.279166% voted for 2
1.355189% voted for 3
1.778786% voted for 4
3.592771% voted for 5
3.952368% voted for 6
1.779186% voted for 7
6.427149% voted for 8
21.280630% voted for 9

For 2th round:
2.889877% voted for 1
34.080927% voted for 2
6.826895% voted for 3
4.990010% voted for 4
5.914753% voted for 5
4.985510% voted for 6
3.302524% voted for 7
11.304360% voted for 8
25.705144% voted for 9

For 3th round:
2.152783% voted for 1
13.005153% voted for 2
21.399772% voted for 3
7.122286% voted for 4
6.122008% voted for 5
6.761774% voted for 6
11.687049% voted for 7
19.607500% voted for 8
12.141674% voted for 9

For 4th round:
2.122183% voted for 1
10.105719% voted for 2
11.917105% voted for 3
17.547460% voted for 4
8.626131% voted for 5
12.079103% voted for 6
18.819449% voted for 7
11.065111% voted for 8
7.717738% voted for 9

For 5th round:
1.689826% voted for 1
7.364821% voted for 2
9.681763% voted for 3
11.704946% voted for 4
20.336237% voted for 5
20.691914% voted for 6
13.062855% voted for 7
9.332565% voted for 8
6.135071% voted for 9

For 6th round:
1.456188% voted for 1
6.726546% voted for 2
10.154619% voted for 3
16.355569% voted for 4
22.985816% voted for 5
17.777558% voted for 6
11.580207% voted for 7
7.757938% voted for 8
5.205558% voted for 9

For 7th round:
1.037992% voted for 1
6.514748% voted for 2
15.437876% voted for 3
22.151823% voted for 4
17.015864% voted for 5
14.029088% voted for 6
11.907505% voted for 7
7.957136% voted for 8
3.947968% voted for 9
\$\endgroup\$
  • 1
    \$\begingroup\$ 1th, 2th, 3th? I'd suggest changing it to print "For round 1" etc. \$\endgroup\$ – Skyler Dec 17 '15 at 18:18
3
\$\begingroup\$

MaxPlayer

A know-it-all. Prefers to remove anybody with high-intelligence (who can therefore challenge his unmatched intellect)

public class MaxPlayer extends Player{

    @Override
    public int vote(Set<Integer> currentOpponents) {
        return Collections.max(currentOpponents);
    }
}
\$\endgroup\$
3
\$\begingroup\$

Guard

Votes off those that pick on the strong ... or those that annoy him.

package WeakestLink.Players;

import WeakestLink.Game.Game;
import WeakestLink.Game.Vote;

import java.util.Set;

/**
 * Created by thenumberone on 12/2/15.
 * @author thenumberone
 */
public class Guard extends Player{

    @Override
    public int vote(Set<Integer> currentOpponents) {
        int me = getSmartness();
        Set<Vote> history = getVotingHistory();
        history.removeIf(vote -> !currentOpponents.contains(vote.getVoter()) || vote.getVoter() == me);
        int[] evilnessLevel = new int[Game.NUMBER_PLAYERS_PER_ROUND];
        for (Vote vote : history){
            evilnessLevel[vote.getVoter()] += vote.getVoted() == me ? 1_000_000 : vote.getVoted();
        }
        int mostEvilOpponent = -1;
        for (int opponent : currentOpponents){
            if (mostEvilOpponent == -1 || evilnessLevel[opponent] > evilnessLevel[mostEvilOpponent]){
                mostEvilOpponent = opponent;
            }
        }
        return mostEvilOpponent;
    }
}
\$\endgroup\$
3
\$\begingroup\$

Leech

Relies on other bots to vote the smartest and dumbest guys off.. sort of.

He is satisfied with turning up somewhere in the middle and eventually splitting the pot with the winner (since he is actually a really decent guy bot).

package WeakestLink.Players;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Set;

public class Leech extends Player {
    /**
     * Copyrighted (not really, use this however you want friends) by Sweerpotato :~)!
     */
    @Override
    public int vote(Set<Integer> currentOpponents) {
        int mySmartness = getSmartness();

        ArrayList<Integer> opponentSmartness = new ArrayList<Integer>();
        opponentSmartness.addAll(currentOpponents);
        opponentSmartness.add(mySmartness);
        Collections.sort(opponentSmartness);

        if(mySmartness > 4 && mySmartness > Collections.min(opponentSmartness)) {
            //There's somebody dumber than me, vote that dude off
            return opponentSmartness.get(opponentSmartness.indexOf(mySmartness) - 1);
        }
        else {
            //Vote off the smartest guy, so we have a better chance to win
            if(mySmartness == Collections.max(opponentSmartness)) {
                //Apparently, we're the smartest guy
                return opponentSmartness.get(opponentSmartness.indexOf(mySmartness) - 1);
            }
            else {
                return Collections.max(opponentSmartness);
            }
        }
    }
}
\$\endgroup\$
  • 2
    \$\begingroup\$ I like. I'm concerned it won't do well though because not many will vote the same as you. That's one flaw with this competition is it seems to me that the other bots force you to conform to a certain type of strategy. \$\endgroup\$ – csga5000 Dec 2 '15 at 21:01
  • 3
    \$\begingroup\$ Still fun though! Winning isn't everything :~)! \$\endgroup\$ – sweerpotato Dec 3 '15 at 16:59
3
\$\begingroup\$

SniperKiller

Another answer shamelessly stolen from Linus's code. This one in will kill all snipers, not protect them. If it knows that no snipers are left it will act like a sniper itself.

package WeakestLink.Players;
import WeakestLink.Game.Vote;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;

public class SniperKiller extends Player {
    boolean[] sniperish;
    int[] sniperwouldvote;

    @Override
    public int vote(Set<Integer> currentOpponents) {
        int smrt = getSmartness();
        currentOpponents.add(smrt);
        if(sniperish==null){
            sniperish = new boolean[10];
            sniperwouldvote = new int[10];
            for(int i=10;--i!=0;){
                sniperish[i] = true;
                sniperwouldvote[i] = 1;
            }
            sniperish[smrt]=false; //knows we are not the sniper
            return 1;
        }
        //figure out who isn't a sniper
        Vote opp_vote;
        int opp_smrt;
        Iterator<Vote> votes = getRecentVotes().iterator();
        while(votes.hasNext()){
            opp_vote = votes.next();
            opp_smrt = opp_vote.getVoter();
            if(opp_vote.getVoted() != sniperwouldvote[opp_smrt])
                sniperish[opp_smrt] = false;
        }
        //figure out how snipers would vote this round
        Iterator<Integer> opps = currentOpponents.iterator();
        int cnt_opp=0, min_opp=10, max_opp=0;
        int[] snpr_votes = new int[10];
        while(opps.hasNext()){
            opp_smrt = opps.next().intValue();
            if(smrt == opp_smrt) continue;
            sniperwouldvote[opp_smrt] = hypothetically(opp_smrt, currentOpponents);
            cnt_opp++;
            if(sniperish[opp_smrt]){
                snpr_votes[sniperwouldvote[opp_smrt]]++;
            }
            if(opp_smrt<min_opp) min_opp=opp_smrt;
            if(opp_smrt>max_opp) max_opp=opp_smrt;
        }
        for(int i = 1;i<10;i++){//hit the weakest sniper.
            if(sniperish[i] && currentOpponents.contains(i))
                return i;
        }
        return hypothetically(smrt, currentOpponents);
    }

    private int hypothetically(int smrt, Set<Integer> currentOpponents) {
        Iterator<Integer> opps = currentOpponents.iterator();
        int cnt_smrt=0, cnt_stpd=0, opp_smrt, min_stpd=10, max_smrt=0;
        while(opps.hasNext()){
            opp_smrt = opps.next().intValue();
            if(opp_smrt > smrt){
                cnt_smrt++;
                if(opp_smrt > max_smrt) max_smrt = opp_smrt;
            }
            else if(opp_smrt < smrt){
                cnt_stpd++;
                if(opp_smrt < min_stpd) min_stpd = opp_smrt;
            }
        }
        if(cnt_stpd>1) return min_stpd;
        return max_smrt;
    }
}
\$\endgroup\$
  • 1
    \$\begingroup\$ I like the idea, but there seems to be a culture of voting for min or max player. voting for someone else might be throwing your vote away. Maybe if you check if the max is sniperish before voting for someone else you'll catch up a bit... (i can't check, on phone) \$\endgroup\$ – Linus Dec 3 '15 at 9:29
2
\$\begingroup\$

RandomPlayer

public class RandomPlayer extends Player{

    @Override
    public int vote(Set<Integer> currentOpponents) {
        return (int) currentOpponents.toArray()[getRandom().nextInt(currentOpponents.size())];
    }
}
\$\endgroup\$
2
\$\begingroup\$

MinPlayer

An Elitist. Prefers to remove anybody with low intelligence.

public class MinPlayer extends Player {

    @Override
    public int vote(Set<Integer> currentOpponents) {
        return Collections.min(currentOpponents);
    }
}
\$\endgroup\$
2
\$\begingroup\$

VengefulSniper

This started as something I thought was original called StupidBuffering (a name I hated to give up), then ended up being just a PrudentSniper who didn't care if he was being targeted. This also seemed to be the only reason he couldn't beat PrudentSniper, so I tweaked things around a bit to make that his focus.

Now, this is basically a sniper, but if the smartest or dumbest bot targets him, he will target whichever one got the most votes last round. If they both got the same number of votes and both targeted him, he goes back to normal sniper-y behavior. In my tests, this actual beats PrudentSniper on occasion.

package WeakestLink.Players;

import java.util.*;

import WeakestLink.Game.Vote;

public class VengefulSniper extends Player{
    @Override
    public int vote(Set<Integer> currentOpponents) {
        int me = getSmartness();
        int smartOpp = Collections.max(currentOpponents);
        int dumbOpp = Collections.min(currentOpponents);
        int votesAgainstSmart=0, votesAgainstDumb=0;
        Boolean targetedBySmart = false, targetedByDumb = false;

        Set<Vote> votesForMe = getRecentVotes();
        Iterator<Vote> votes = votesForMe.iterator();
        while(votes.hasNext()){
            Vote vote = votes.next();
            int voter = vote.getVoter();
            int voted = vote.getVoted();

            if(voted == me){
                if(voter == smartOpp){
                    targetedBySmart = true;
                }
                if(voter == dumbOpp){
                    targetedByDumb = true;
                }
            } else if (voted == smartOpp){
                votesAgainstSmart++;
            } else if (voted == dumbOpp){
                votesAgainstDumb++;
            }
        }

        // If being targeted by smartest or dumbest, take them out
        // Try to go with the rest of the crowd if they both targeted me
        if(targetedBySmart ^ targetedByDumb){
            return targetedBySmart ? smartOpp : dumbOpp;
        } else if (targetedBySmart && targetedByDumb){
            if (votesAgainstSmart > votesAgainstDumb){
                return smartOpp;
            } else if (votesAgainstDumb > votesAgainstSmart){
                return dumbOpp;
            }
        }

        Iterator<Integer> opps = currentOpponents.iterator();
        int cnt_stpd=0;
        while(opps.hasNext()){
            int opp_smrt = opps.next().intValue();
            if(opp_smrt < me){
                cnt_stpd++;
            }
        }

        if (cnt_stpd < 2 || (currentOpponents.size() < 4)){ //buffer is small, protect myself
            return smartOpp;
        } else {
            return dumbOpp;
        }
    }
}
\$\endgroup\$
2
\$\begingroup\$

MiddleMan

The MiddleMan trys his best to maximize profits while keeping a wary eye that he's not cut from the game. He keeps around lesser contestants to improve his chance of making it to the next round (and to leave an easy finish). He will vote off someone smarter than him only if there are more smarter contestants than there are lesser contestants. Whichever of the two groups, he always votes for the lowest of the group to keep the pot climbing.

package WeakestLink.Players;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;
public class MiddleMan extends Player {
    @Override
    public int vote(Set<Integer> currentOpponents) {
        int smrt = getSmartness();

        //count number of players smarter/stupider than me
        Iterator<Integer> opps = currentOpponents.iterator();
        int cnt_smrt=0, cnt_stpd=0, opp_smrt, min_stpd=9, min_smrt=9;

        while(opps.hasNext()){
            opp_smrt = opps.next().intValue();
            if(opp_smrt > smrt){
                cnt_smrt++;
                if(opp_smrt < min_smrt) min_smrt = opp_smrt;
            }
            else if(opp_smrt < smrt){
                cnt_stpd++;
                if(opp_smrt < min_stpd) min_stpd = opp_smrt;
            }
        }

        //Keep myself in the middle of the pack, favoring the point earners
        if(cnt_stpd>cnt_smrt)
            return min_stpd;
        else
            return min_smrt;
    }
}

P.S. hope it compiles, I'm not a Java guy.

Had this scheme in mind before reading the other entries. Then I was surprised how close (but critically different) Sniper was so I went ahead and used that as a jumping off point since I don't know Java syntax. Thanks @Linus

\$\endgroup\$
  • 1
    \$\begingroup\$ Please test your code. don't try to write answers in languages you do not know \$\endgroup\$ – TanMath Dec 4 '15 at 5:06
  • \$\begingroup\$ @TanMath - Thanks for your input. I have much experience in C/Java-like languages, however not Java specifically, so I'm quite confident that my code is in fact correct and will work. That being said, if there is an error in it and it won't run I wont be offended if the game master disqualifies the entry. \$\endgroup\$ – tbernard Dec 4 '15 at 5:10
  • \$\begingroup\$ You're right. Thanks @Linus. Edited. \$\endgroup\$ – tbernard Dec 4 '15 at 8:22
  • 1
    \$\begingroup\$ @tbernard I'm happy to fix bugs, but your code didn't have any :) \$\endgroup\$ – Nathan Merrill Dec 4 '15 at 14:56
  • \$\begingroup\$ Ouch. Didn't do as well as I'd hoped. I did seem to help out sniper though, so that's something I guess haha. \$\endgroup\$ – tbernard Dec 4 '15 at 22:02
2
\$\begingroup\$

ApproximatePosition

This bot is trying to shooting approximately around the missing smartness values, assuming that the group will continue with the same pattern, meaning it will target the same type of target. It always vote for the smartest of two player when there's a choice.

Long time I didn't use Java, and currently at work so... Can't test it, hope it's not too buggy, be gentle please :).

By the way, it uses awt.Point only because I'm too lazy to implement a tuple n_n.

package WeakestLink.Players;
import WeakestLink.Game.Vote;

import java.util.*;
import java.awt.Point;

public class ApproximatePosition extends Player
{

    @Override
    public int vote(Set<Integer> currentOpponent)
    {
        List<Integer> present = new ArrayList<>(currentOpponent);
        List<Integer> emptyPosition = new ArrayList<Integer>();
        Collections.sort(present);

        //If it is the first round, vote for the smartest buddy
        if(present.size()==8)
            return present.get(present.size()-1);


        int lastCheck=present.get(0);
        if(lastCheck>0)
            for(int i=0;i<lastCheck;i++)
                if(i!=getSmartness()&&!emptyPosition.contains(i))
                    emptyPosition.add(i);
        for(int i=1;i<present.size();i++)
        {
            if(present.get(i)-lastCheck>1)
                for (int j=lastCheck+1;j<present.get(i);j++)
                    if(j!=getSmartness()&&!emptyPosition.contains(j))
                        emptyPosition.add(j);
            lastCheck=present.get(i);
        }
        //untill there's at least 3 excluded members, we continue with this behaviour
        if(emptyPosition.size()<=2)
        {
            if(emptyPosition.isEmpty()) return present.get(present.size()-1);
            return decide(emptyPosition.get(0),present.get(present.size()-1),present.get(0),present);
        }

        Point maxRangeOfBlank=new Point(present.get(present.size()-1),present.get(present.size()-1));
        for (int i=0;i<emptyPosition.size()-1;i++)
            if(emptyPosition.get(i+1)-emptyPosition.get(i)==1)
            {
                int size=0;
                while(i+size+1<emptyPosition.size() && emptyPosition.get(i+size+1)-emptyPosition.get(i+size)==1)
                    size++;
                if(size>=sizeOfRange(maxRangeOfBlank))
                    maxRangeOfBlank=new Point(emptyPosition.get(i),emptyPosition.get(size));
                i+=size;
            }

        return decide(maxRangeOfBlank,present.get(present.size()-1),present.get(0),present);
    }

    private int decide(int blankSeat, int smartest,int dumbest,List<Integer> present)
    {
        return decide(new Point(blankSeat,blankSeat),smartest,dumbest,present);
    }

    private int decide(Point rangeBlankSeat, int smartest,int dumbest,List<Integer> present)
    {
        int target= smartest;
        if (rangeBlankSeat.getY()==smartest||((int)rangeBlankSeat.getY()+1)==getSmartness()){
            if ((rangeBlankSeat.getX()==dumbest||(int)rangeBlankSeat.getX()-1==getSmartness())){
                target= smartest; //should not happen
            } else {
                target= (int) rangeBlankSeat.getX()-1; //Vote for dumber than the missing
            }
        } else {
            target= (int) rangeBlankSeat.getY() +1; //Vote for smarter than the missing, default comportment
        }
        if(present.contains(target))
            return target;
        return smartest;
    }
    //Return the number of consecutive values between X and Y (included)
    private int sizeOfRange(Point range)
    {
        return (int)(range.getY()-range.getX())+1;
    }

}
\$\endgroup\$
  • \$\begingroup\$ So, there were some bugs. :) First, unfortunately, the Integer[] cast doesn't work, it has to be an Object[] cast (which I don't like). So I wrapped it all into an ArrayList instead of an array. Secondly, this line: emptyPosition[emptyPosition.length]=j; will always give you array out of bounds. Finally, not sure why, but you will vote off players that aren't in the round. \$\endgroup\$ – Nathan Merrill Dec 2 '15 at 13:59
  • \$\begingroup\$ Oh, also, your ternary block was returning a double instead of int, and was super convoluted, I turned it into a standard if/else. You can find all of my changes on Github: github.com/nathanmerrill/WeakestLink/blob/master/src/… \$\endgroup\$ – Nathan Merrill Dec 2 '15 at 14:05
  • \$\begingroup\$ @NathanMerrill Wow, thanks a lot. For the emptyPosition[emptyPosition.length], it is a dumb mistake as length is always one over the last index ^^. Thanks for the changes, I will use this new version to correct it. About the ternary block... yeah, felt like using it, and maybe way too used to writing for myself, wasn't handy to read I guess. Doing the corrections and updating it. \$\endgroup\$ – Katenkyo Dec 2 '15 at 14:11
2
\$\begingroup\$

SniperAide

Before the addition of PrudentSniper I wrote a bot to help Sniper beat AntiExtremist and other frauds (I use the word with love). The bot, SniperAide, looks for players who vote like snipers and votes as it thinks they would when there is a consensuses. If all players look like snipers he votes for the max, protecting lower Snipers (who would also switch to the max at this point), even if it is himself.

The Code:

package WeakestLink.Players;
import WeakestLink.Game.Vote;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;

public class SniperAide extends Player {
    boolean[] sniperish;
    int[] sniperwouldvote;

    @Override
    public int vote(Set<Integer> currentOpponents) {
        int smrt = getSmartness();
        if(sniperish==null){
            sniperish = new boolean[10];
            sniperwouldvote = new int[10];
            for(int i=10;--i!=0;){
                sniperish[i] = true;
                sniperwouldvote[i] = 1;
            }
            sniperish[smrt]=false; //knows we are not the sniper
            return 1;
        }
        //figure out who might isn't a sniper
        Vote opp_vote;
        int opp_smrt;
        Iterator<Vote> votes = getRecentVotes().iterator();
        while(votes.hasNext()){
            opp_vote = votes.next();
            opp_smrt = opp_vote.getVoter();
            if(opp_vote.getVoted() != sniperwouldvote[opp_smrt])
                sniperish[opp_smrt] = false;
        }
        //include ourself in the simulation of other snipers.
        currentOpponents.add(smrt);
        //figure out how snipers would vote this round
        Iterator<Integer> opps = currentOpponents.iterator();
        int cnt_snpr=0, cnt_opp=0, min_opp=10, max_opp=0;
        int[] snpr_votes = new int[10];
        while(opps.hasNext()){
            opp_smrt = opps.next().intValue();
            if(smrt == opp_smrt) continue;
            sniperwouldvote[opp_smrt] = hypothetically(opp_smrt, currentOpponents);
            cnt_opp++;
            if(sniperish[opp_smrt]){
                cnt_snpr++;
                snpr_votes[sniperwouldvote[opp_smrt]]++;
            }
            if(opp_smrt<min_opp) min_opp=opp_smrt;
            if(opp_smrt>max_opp) max_opp=opp_smrt;
        }
        //figure out how to vote in sniper's intrest when not identified
        if(cnt_snpr == cnt_opp)
            return max_opp;
        if(cnt_snpr == 0)
            return hypothetically(smrt, currentOpponents);
        //if multiple hypothetical snipers only vote how they agree
        int onlyvote = -1;
        for(int i=10; --i!=0;){
            if(onlyvote>0 && snpr_votes[i]!=0) onlyvote=-2;
            if(onlyvote==-1 && snpr_votes[i]!=0) onlyvote=i;
        }
        if(onlyvote>0) return onlyvote;
        return max_opp;
    }

    private int hypothetically(int smrt, Set<Integer> currentOpponents) {
        Iterator<Integer> opps = currentOpponents.iterator();
        int cnt_smrt=0, cnt_stpd=0, opp_smrt, min_stpd=10, max_smrt=0;
        while(opps.hasNext()){
            opp_smrt = opps.next().intValue();
            if(opp_smrt > smrt){
                cnt_smrt++;
                if(opp_smrt > max_smrt) max_smrt = opp_smrt;
            }
            else if(opp_smrt < smrt){
                cnt_stpd++;
                if(opp_smrt < min_stpd) min_stpd = opp_smrt;
            }
        }
        if(cnt_stpd>1) return min_stpd;
        return max_smrt;
    }
}

He currently is not much help against PrudentSniper.

\$\endgroup\$
  • \$\begingroup\$ Based on your description and theory I don't see how this would help any sniper-like bot beat any other snipers.. if anything wouldn't it just protect all snipers? Sorry I don't have more time to dig into your code and really understand it for myself. \$\endgroup\$ – csga5000 Dec 2 '15 at 21:55
  • \$\begingroup\$ @csga5000, Since you may rarely identify Sniper by voting record now it does protect them all a little bit. But when a difference is clear it always acts in the Snipers interest, so it mostly a kind of tie-breaker. The focus on winning is macroscopic games, not individual rounds, in most rounds it really can't do anything more than maintain the coin-flip situation. \$\endgroup\$ – Linus Dec 2 '15 at 22:15
1
\$\begingroup\$

HighOrLowNotSelf

Removes randomly lowest or highest intelligence player (but not self).

public class HighOrLowNotSelf extends Player{
    @Override
    public int vote(Set<Integer> ops) {
        int b=Math.round(Math.random()*1);
        int p;
        if(b==1) p=Collections.max(ops) else p=Collections.min(ops);
        if(p==getSmartness()) {
            return vote(ops);
        }
        return p;
    }
}
\$\endgroup\$
  • \$\begingroup\$ So, there are a couple of bugs with this submission. First, Math.round() returns a long, not int. Secondly, ops does not contain yourself. (If you wanted to vote for yourself, you'd explicitly have to include it). Finally, the if/else you included isn't valid Java. I've fixed your code, and added it to github \$\endgroup\$ – Nathan Merrill Dec 3 '15 at 20:14
1
\$\begingroup\$

Anarchist

The anarchist doesn't like regimes.
The anarchist will try to kill the current president.
If the anarchist is president, he decides to abuse his power and kill useless peagents. Unless he was targeted by one of his inferiors, for they should burn instead.

package WeakestLink.Players;

import WeakestLink.Game.Vote;

import java.util.LinkedList;
import java.util.Set;

public class Anarchist extends Player {

    LinkedList<Integer> opponents;

    @Override
    public int vote(Set<Integer> currentOpponents) {
        opponents = new LinkedList();
        opponents.addAll(currentOpponents);
        opponents.sort(Integer::compare);

        int me = getSmartness();

        if (getPresident() != me) {
            return getPresident();
        } else {
            // treason ?
            Vote voteForMe = getRecentVotes().stream().filter(v -> v.getVoted() == me).findAny().orElse(null);
            if (voteForMe == null) {
                // No treason ! Hurray. Kill the peagants.
                return getPeagant();
            } else {
                // TREASON!
                return opponents.get(opponents.indexOf(voteForMe.getVoter()));
            }
        }
    }

    private int getPresident() {
        return opponents.getLast();
    }

    private int getPeagant() {
        return opponents.getFirst();
    }

}
\$\endgroup\$
1
\$\begingroup\$

IndependentVoter

This bot knows that the general population is always wrong! So it votes for whoever is getting the least votes.

Code is nearly Identical to SolarAaron's "Bandwagon", but the end logic is flipped.

package WeakestLink.Players;

import WeakestLink.Game.Vote;
import java.util.Map;
import java.util.Set;

/**
 * Votes for the currently lest voted bot in the game.
 * Or the lowest one.
 */
public class IndependentVoter
        extends Player {

    @Override
    public int vote(Set<Integer> currentOpponents) {
        int self = getSmartness(), vote = -1;
        java.util.Map<Integer, Integer> votes = new java.util.TreeMap<>();
        getVotingHistory().stream().map((Vote v)-> v.getVoted()).filter((Integer i)-> !i.equals(self)).forEach((Integer tgt)-> {
            if(!votes.containsKey(tgt)) {
                votes.put(tgt, 1);
            } else {
                votes.put(tgt, votes.get(tgt) + 1);
            }
        });

        do {
            if(votes.entrySet().isEmpty()) {
                vote = currentOpponents.stream().filter((Integer i)-> !i.equals(self)).sorted().findFirst().get();
            } else {
                if(votes.containsKey(vote)) {
                    votes.remove(vote);
                    vote = -1;
                }

                for(Map.Entry<Integer, Integer> vv: votes.entrySet()) {
                    Integer key = vv.getKey();
                    Integer value = vv.getValue();

                    if((vote == -1) || (value < votes.get(vote))) {
                        vote = key;
                    }
                }
            }
        } while(!currentOpponents.contains(vote));

        return vote;
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.