22
\$\begingroup\$

COMPLETE

Wumpus season has ended, and with, the lives of many a vicious Wumpus and brave Hunter. As well as some cowardly, immoral and downright stupid Hunters. But at the end of the day, NascarHunter by Dr Heckle came away with the most loot and glory. We salute you, oh brave... driver?

It's open season on Wumpuses, and hunters from around the world are flocking to the Wumpus Caves to try to get rich of off valuable Wumpus Pelts.

The Game

Based on the classic Hunt the Wumpus game, with a few variations.

The Map

A dodecahedron. There are 20 rooms, each connected to 3 other room, making basically 3 rings with bridges in between.

The Wumpus

The Wumpus is a mystical beast. None are sure what it looks like, but all agree that it is ferocious. The Wumpus lives in the caves, and likes to eat dead things. If there is a corpse in a cave next to the Wumpus, he will move there and eat it. If the Wumpus happens to move into a cave containing hunters, he will kill and eat them as well. If the Wumpus hears an arrow near him, he will panic and run into a random room.

Optional traits: These are currently included to make the game more interesting, but can be removed at popular request if they add too much randomness.

Enraged Wumpus: If the Wumpus is shot, he has a 20% of surviving and rampaging for 3 days. When rampaging, he will randomly move twice a day, but still be attracted to corpses. After 3 days, he will die. A second arrow shot will also kill the Wumpus.

Wandering Wumpus: Each day, if nothing else triggers the Wumpus, he has a 25% chance of moving.

The Hunters

Four hunters can enter the caves at a time. The caves are dark, so the hunters cannot see, but they can use their other senses. Hunters can smell the Wumpus in an adjacent room, smell a corpse in an adjacent room, hear other hunters moving in adjacent rooms, and hear if an arrow is shot into an adjacent room.

Hunters have two actions: Shoot or move. A hunter can shoot an arrow into an adjacent room or his own room, and move similarly.

Gameplay

In each round, hunters will first take note of their surroundings, and then make one move. Movement occurs before arrows, so if an arrow is shot into a room that a hunter is moving out of, the hunter will survive. After all hunter moves have been made, the arrows are evaluated. If a single occupant and arrow are in the same room, the occupant will be shot and die. If two or more occupants share a room, one will randomly be hit and die. Note: Corpses are occupants, so a dead body may serve as a partial shield.

After all hunter moves, the Wumpus will respond. If a hunter has moved into the room with the Wumpus, the Wumpus will eat him/her. If the Wumpus moves, it will also eat the occupants of the new room.

After 100 days, if the hunters have not killed the Wumpus, or fallen victim, they will die of starvation within the caves.

The Code

All source code can be found here. All submissions should be in Java, unless somebody wants to write me a stdIn/stdOut adaptor ;)

Bots should extend the Hunter class. To name your Hunter, add a constructor with no parameters that sets the name field. T o respond, override the getResponse function. In this function, each turn, you will be passed an array of 5 booleans that tells you about your surroundings.

status 0 = "You smell a wumpus"

status 1 = "You hear another hunter"

status 2 = "You smell a corpse"

status 3 = "You hear an arrow clatter"

status 4 = "You feel another hunter in the same room as you"

The Hunter class has 2 slots: nextMove and nextDirection, that use the enums Move and Direction respectively. Move can either be MOVE or SHOOT, direection can be LEFT, RIGHT, BACK, or HERE. Directions are consistent, meaning back will always return you to the previous room you were in, and if entering from the same room, LEFT and RIGHT will always be the same. However, if you enter from a different direction, LEFT and RIGHT will take you to different places.

Feel free to implement your own methods as well. Memory is persistent throughout the whole tournament, meaning your hunter will only be constructed once. However, at the start of each round, the newGame() method is called to let your hunter know a new game has started.

Scoring

Four hunters compete at a time. If killed, the Wumpus pelt is worth 60 points, split evenly among all surviving hunters. Full round robin style, so each combination of 4 hunters will play together.

5 sample Hunters are included: 4 with very basic functionality, and one that allows user input to play, for testing purposes.

If you have any suggestions/requests, please let me know!

The End is in sight!

The worlds Wumpus population is being hunted at an alarming rate. The Protect Our Wumpuses Act is expected to be passed on Wednesday, June 17th. After this date, no more hunters will be allowed in the caves, and a winner will be chosen.

Scoreboard

Note : My apologies, there was a bug in the code that could cause Hunters to stay dead through multiple games. Having fixed it, it does not change the overall rankings very much, but does largely change the stats.

Scores are in, running each set of hunter in 1000 games each. NascarHunter and FullCoverageHunter lead the pack, and although the addition of AntiHunter has given the Wumpuses a 2% survival boost, they are still shaking in their suckered feet at the 32% survival rate of their kind. Arrows from fellow hunters are more than ever the biggest threat in the caves.

1. NascarHunter : 16557175 (17.08)
2. FullCoverageHunter : 15195545 (15.68)
3. Trig : 14459385 (14.92)
4. Idomeneus : 13428570 (13.85)
5. Eats Shoots and Leaves : 12763945 (13.17)
6. WalkingIdiot : 12329610 (12.72)
7. NealHunter : 12287140 (12.68)
8. Unnamed : 11013720 (11.36)
9. MonsterHunter : 10686035 (11.02)
10. Pacer : 9187000 (9.48)
11. Badger : 9042570 (9.33)
12. Stay : 8721210 (9.0)
13. Emo Wumpus : 7831050 (8.08)
14. Paranoid : 7722965 (7.97)
15. Huddle : 7465420 (7.7)
16. ElmerFudd : 7245995 (7.47)
17. Laomedon : 6963845 (7.18)
18. Pacifist : 6944960 (7.16)
19. ScaredyCat : 6937370 (7.15)
20. Wumpus : 114170 (0.11)



Total rounds: 4845000
Humans killed by arrows: 5289674 (1.09)
Humans dead by starvation: 1046024 (0.21)
Humans killed by wumpus: 1887634 (0.38)
Wumpus victories: 1563372 (0.32)
\$\endgroup\$
  • 1
    \$\begingroup\$ Can you hit another hunter with an arrow? \$\endgroup\$ – MegaTom Jun 3 '15 at 21:48
  • 1
    \$\begingroup\$ Yessir, or yourself if you shoot an arrow at HERE \$\endgroup\$ – Cain Jun 3 '15 at 21:52
  • 1
    \$\begingroup\$ Instead of an array for 5 booleans, why not just pass an int valued 0-31? We're big boys here, we can do bit operations :) \$\endgroup\$ – DoctorHeckle Jun 4 '15 at 14:05
  • 1
    \$\begingroup\$ @DoctorHeckle Sure we are, but it's still more dev-friendly to have an array rather than a single int ^^. \$\endgroup\$ – Katenkyo Jun 4 '15 at 14:49
  • 1
    \$\begingroup\$ For arrows and corpses, if one is in the same room as you you get the same status as if it is adjacent. Hunters are the only ones you can distinguish between being next to the room or in the room. \$\endgroup\$ – Cain Jun 4 '15 at 19:17

12 Answers 12

11
\$\begingroup\$

NascarHunter

Not much logic to it. Its rules are simple: turn left, scorched earth, repeat. And, hey, he might get lucky!

8 June edit:

Added in additional logic to Nascar to account of adjacency to a Wumpus relative to its last move. Consider it a pit stop, to stay in theme. If it sense a Wumpus after shooting down a hallway, then it must have arrived in one of the other two adjacent rooms, since it would've died to the shot the hunter just took. It effectively give either the Wumpus 2 turns to live if it doesn't move again, or Nascar 1 turn to live if he's standing on a corpse. Also accounts for a third option if it's the first turn, but just the once. I'll port to to FCH eventually, busy busy.

package Hunters;

import Mechanics.*;

public class NascarHunter extends Hunter {

    private int state;
    private boolean shootHall;
    private boolean newGame;

    public NascarHunter(){

        name = "NascarHunter";
        state = 0;
        shootHall = true;
        newGame = true;

    }

    public void newGame(){

        state = 0;
        newGame = true;

    }

    public void getResponse(boolean[] status){

        // Wumpus about - stand and deliver
        if( status[0] ){

            nextMove = Move.SHOOT;

            switch( state ){

            case 0: // Must be in either Right or Back
                if(newGame){

                    // try Left if this is the first turn, just in case
                    nextDirection = Direction.LEFT;
                    newGame = false;

                }
                else if(shootHall) nextDirection = Direction.BACK;
                else               nextDirection = Direction.RIGHT;
                shootHall = !shootHall;
                break;
            case 2: // Must be in either Left or Back
                if(newGame){

                    // try Right if this is the first turn, just in case
                    nextDirection = Direction.RIGHT;
                    newGame = false;

                }
                else if(shootHall) nextDirection = Direction.BACK;
                else               nextDirection = Direction.LEFT;
                shootHall = !shootHall;
                break;
            default: // Must be in either Left or Right
                if(newGame){

                    // try Back if this is the first turn, just in case
                    nextDirection = Direction.BACK;
                    newGame = false;

                }
                else if(shootHall) nextDirection = Direction.RIGHT;
                else               nextDirection = Direction.LEFT;
                shootHall = !shootHall;
                break;

            }

        }else{

            // disregard state, shove out and light 'em up!
            switch( state ){

            case 0: // move out
                nextMove = Move.MOVE;
                nextDirection = Direction.LEFT;
                state++;
                break;
            case 1: // shoot right
                nextMove = Move.SHOOT;
                nextDirection = Direction.RIGHT;
                state++;
                break;
            case 2: // shoot behind
                nextMove = Move.SHOOT;
                nextDirection = Direction.BACK;
                state++;
                break;
            case 3: // shoot left
                nextMove = Move.SHOOT;
                nextDirection = Direction.LEFT;
                state = 0;
                break;

            }

        }

    }

}

FullCoverageHunter

Follows the same credo of the NascarHunter, but alternates his pathing, which is guaranteed to form a loop 10 unique rooms long. Since every room is unique, and we fire at every direction per room, all rooms get fired into. This is true for any starting room (postulated by my buddy Neal, thanks Neal!). Dodecahedra are pretty cool like that!

I'd like to point out that this is is different than our friend, the MonsterHunter, as he doesn't try any trickery or any real "tactics." The strength of this is the fact that every room gets fired into, again: the brute force solution. This has a theoretical leg up on the NascarHunter, since Nascar will only hit 10 of the 20 rooms, covering only half the area.

11 June edit:

Added in Wumpus detection logic pattern from NascarHunter. Should objectively improve score.

package Hunters;

import Mechanics.*;

public class FullCoverageHunter extends Hunter {

    private int state;
    private boolean headLeft;
    private boolean shootHall;
    private boolean newGame;

    public FullCoverageHunter(){

        name = "FullCoverageHunter";
        state = 0;
        headLeft = false;
        shootHall = true;

    }

    public void newGame() {
        state = 0;
        headLeft = false;
        newGame = true;
    }


    public void getResponse(boolean[] status){

        // Wumpus about - stand and deliver
        if( status[0] ){

            nextMove = Move.SHOOT;

            switch( state ){

            case 0: // Must be in either Right or Back
                if(newGame){

                    // try Left if this is the first turn, just in case
                    nextDirection = Direction.LEFT;
                    newGame = false;

                }
                else if(shootHall) nextDirection = Direction.BACK;
                else               nextDirection = Direction.RIGHT;
                shootHall = !shootHall;
                break;
            case 2: // Must be in either Left or Back
                if(newGame){

                    // try Right if this is the first turn, just in case
                    nextDirection = Direction.RIGHT;
                    newGame = false;

                }
                else if(shootHall) nextDirection = Direction.BACK;
                else               nextDirection = Direction.LEFT;
                shootHall = !shootHall;
                break;
            default: // Must be in either Left or Right
                if(newGame){

                    // try Back if this is the first turn, just in case
                    nextDirection = Direction.BACK;
                    newGame = false;

                }
                else if(shootHall) nextDirection = Direction.RIGHT;
                else               nextDirection = Direction.LEFT;
                shootHall = !shootHall;
                break;

            }

        }else{

            // disregard state, shove out (in an alternating fashion!) and light 'em up!
            switch( state ){

            case 0: // move out, change alternation state
                nextMove = Move.MOVE;
                if(headLeft) nextDirection = Direction.LEFT;
                else         nextDirection = Direction.RIGHT;
                state++;
                headLeft = !headLeft;
                break;
            case 1: // shoot into non-exit path
                nextMove = Move.SHOOT;
                if(headLeft) nextDirection = Direction.RIGHT;
                else         nextDirection = Direction.LEFT;
                state++;
                break;
            case 2: // shoot behind
                nextMove = Move.SHOOT;
                nextDirection = Direction.BACK;
                state++;
                break;
            default: // shoot into next room,
                nextMove = Move.SHOOT;
                if(headLeft) nextDirection = Direction.LEFT;
                else         nextDirection = Direction.RIGHT;
                state = 0;
                break;

            }

        }

    }

}

Let me know if there're any errors, package didn't play nice with my IDE :(

\$\endgroup\$
  • 1
    \$\begingroup\$ I'm pretty sure this should be called MadMaxHunter, as I don't recall too many NASCAR races involving gunfire between vehicles. Seems to do well! \$\endgroup\$ – Ralph Marshall Jun 4 '15 at 21:10
  • \$\begingroup\$ I had to put brackets around the headLeft in the if statements to get FullCoverageHunter to work. Both of your bots do very well- NascarHunter is slightly better \$\endgroup\$ – euanjt Jun 5 '15 at 9:43
  • \$\begingroup\$ The variable newGame in FullCoverageHunter seems to never be declared. I added a private boolean newGame and set it to true in the newGame() method, is that what you intended? \$\endgroup\$ – Cain Jun 12 '15 at 15:55
  • \$\begingroup\$ @Cain whoops! Yeah big oversight from me, I'll edit that in here, my bad. \$\endgroup\$ – DoctorHeckle Jun 12 '15 at 16:00
7
\$\begingroup\$

Badger

He doesn't like visitors.

package Hunters;

import Mechanics.*;

public class Badger extends Hunter {

    public Badger(){
        name = "Badger";
    }

    @Override
    public void getResponse(boolean[] status){
        nextMove = Move.SHOOT;
        nextDirection = Direction.values()[((int) (Math.random() * 3))];
    }
}
\$\endgroup\$
6
\$\begingroup\$

Elmer Fudd

"Shhh. Be vewy vewy quiet, I'm hunting wumpus"

Elmer is fixated on the prey and ignores everything except corpses and the Wumpus. He tries to find a corpse then backs off and starts shooting. If he smells a Wumpus, he will back off and if he still smells him he will shoot.

My apologies to all the java programmers, this is probably extremely ugly, full of syntax errors and I have probably messed up my logic.

package Hunters;

import Mechanics.*;

public class ElmerFudd extends Hunter {

    private state int;
    private previousDir int;

    public ElmerFudd(){
        name = "ElmerFudd";
    }

    public void newGame() {
        state=0;
        previousDir = Direction.LEFT;
    }

    public void getResponse(boolean[] status){

        nextMove = Move.MOVE;
        switch (previousDir) {
            case Direction.LEFT:
                nextDirection = Direction.RIGHT;
                break;
            case Direction.RIGHT:
                nextDirection = Direction.LEFT;
                break;
        }   

        if(status[2]&&state==0) {
            state = 1;
            return;
        }

        if(state==1){
            if(status[2]){
                state=2;
            };
            nextDirection = Direction.BACK;
            return;
        }

        if(state==2){
            nextMove = Move.SHOOT;
            nextDirection = Direction.BACK;
            return;
        }

        if(state==3&&status[0])
            nextMove = Move.SHOOT;
            nextDirection = Direction.BACK;
            return;
        }

        if(state==3) {
            state = 0;
        }

        if(status[0]){
            state=3;
            nextDirection = Direction.BACK;
        }

    }
}

Pacifist

This guy is morally against any sort of blood sport and wondering how he turned up in this situation at all. He will run away from any sort of contact and never take a shot.

package Hunters;

import Mechanics.*;

public class Pacifist extends Hunter {


    public Pacifist(){
        name = "Pacifist";
    }

    public void getResponse(boolean[] status){
        nextMove = Move.MOVE;
        if(status[0]||status[1]||status[2]||status[3]||status[4]){
            nextDirection = Direction.values()[((int) (Math.random() * 3))];
            return;
        }
        nextDirection = Direction.HERE;
    }
}
\$\endgroup\$
  • 1
    \$\begingroup\$ I think this might work. All you have to do in a game is hope someone gets the wumpus before it gets you and you get full credit. \$\endgroup\$ – Ralph Marshall Jun 5 '15 at 0:03
  • 1
    \$\begingroup\$ That was the general idea. Let the others do the hard work:-) \$\endgroup\$ – MickyT Jun 5 '15 at 0:06
  • 1
    \$\begingroup\$ That's why I waited for you to code this, even though I thought of it first :-) \$\endgroup\$ – Mawg Mar 7 '17 at 13:03
5
\$\begingroup\$

MonsterHunter

We are hunting a monster and we are 4... It reminds me of my favorite game ! This hunter will walk most of the map by moving left-right alternatively, and if the Wumpus is near, he will lure him by going backward to be able to determine precisely were it is.

I could get rid of lastDirection, but I keep for semantics and readability :). Actually, it dies quite a lot, but the controller often put 2/3 hunter in the same room at the start, and often with the Wumpus near (can also be in the same room)... so insta death ^^'.

package Hunters;

import Mechanics.*;

public class MonsterHunter extends Hunter 
{
    private Direction lastDirection=Direction.HERE;
    private boolean[] lastStatus=new boolean[5];
    private int   shooted=0;
    private boolean   walkMode=true;
    private int         turnStayed=0;

    public MonsterHunter(){
        super();
        name = "MonsterHunter";
    }

    @Override
    public void getResponse(boolean[] status)
    {
        if(status[0])
        {
            if(!lastStatus[0]||shooted==0)
            {
                nextDirection=(walkMode)?Direction.RIGHT:Direction.LEFT;;
                nextMove=Move.SHOOT;
            }
            else if(lastStatus[0]&&shooted==1)
            {
                nextDirection=Direction.BACK;
                nextMove=Move.MOVE;
            }
            else
            {
                nextDirection=Direction.BACK;
                nextMove=Move.SHOOT;
            }
        }

        else if(status[2])
        {
            nextMove=Move.MOVE;
            if(Math.random()*6<turnStayed)
            {
                nextDirection=Direction.HERE;
                turnStayed++;
            }
            else
                nextDirection=(walkMode)?Direction.RIGHT:Direction.LEFT;
        }
        else
        {
            nextMove=(!status[1]&&Math.random()<0.5)?Move.MOVE:Move.SHOOT;
            nextDirection=(walkMode)?Direction.RIGHT:Direction.LEFT;
        }

        if(nextMove==Move.MOVE)
        {
            if(shooted>0)
                walkMode=walkMode^(shooted>0);
            if(lastStatus[0]&&shooted==1)
                shooted++;
            else
                shooted=0;
            lastDirection=nextDirection;
        }
        else
            shooted++;
        for(int i=0;i<status.length;i++)
            lastStatus[i]=status[i];
    }
}
\$\endgroup\$
  • \$\begingroup\$ Thanks for pointing that out, controller is fixed so that each occupant has a unique start \$\endgroup\$ – Cain Jun 4 '15 at 14:58
4
\$\begingroup\$

PacingHunter

Back and forth, between to rooms. if it smells a Wumpus or hears a player it shoots left then right. if a Wumpus is next to him to start with than he shoots the room he will move to.

package Hunters;

import Mechanics.Direction;
import Mechanics.Hunter;
import Mechanics.Move;

public class PacingHunter extends Hunter {

    int state = 0;//Pacing
    int turn = 0;

    public PacingHunter() {
        name = "Pacer";
    }

    public void newGame() {
        turn =  0;
        state = 0;
    }

    public void getResponse(boolean[] status){
        turn += 1;
        if(state == 0 && status[0] && turn == 1){
            nextMove = Move.SHOOT;
            nextDirection = Direction.BACK;
            return;
        }
        if(state == 0 &&(status[0] || status[1])){
            nextMove = Move.SHOOT;
            nextDirection = Direction.LEFT;
            state = 1;
            return;
        }
        if(state == 1 && (status[0] || status[1])){
            nextMove = Move.SHOOT;
            nextDirection = Direction.RIGHT;
            state = 0;
            return;
        }
        if(status[1] && state == 0){
            nextMove = Move.SHOOT;
            nextDirection = Direction.BACK;
            state = 0;
            return;

    }
    nextMove = Move.MOVE;
    nextDirection = Direction.BACK;
}

}

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

ScaredyCat

ScaredyCat is scared of everything. If it smells a wumpus or a corpse or hears an arrow or a hunter, it runs to a random direction. Otherwise, it simply keeps shooting arrows in random directions.

package Hunters;

import Mechanics.*;

public class ScaredyCat extends Hunter {

    public ScaredyCat(){
        name = "ScaredyCat";
    }

    @Override
    public void getResponse(boolean[] status){

        for(int i=0; i<status.length; i++)
            if(status[i])
            {
                nextMove = Move.MOVE;
                nextDirection = Direction.values()[((int) (Math.random() * 3))];
                return;
            }

        nextMove = Move.SHOOT;
        nextDirection = Direction.values()[((int) (Math.random() * 3))];
    }
}
\$\endgroup\$
  • 6
    \$\begingroup\$ Lol I don't know if this was intentional, but he will scare himself by shooting arrows and then run. \$\endgroup\$ – Cain Jun 4 '15 at 16:35
4
\$\begingroup\$

Eats Shoots and Leaves

Unlike the Panda of grammar book fame, this hunter doesn't actually eat anything, but we either shoot if the Wumpus is about or we leave on a trail that should hopefully keep us from walking in circles.

package Hunters;

import java.util.Random;

import Mechanics.Hunter;
import Mechanics.Move;
import Mechanics.Direction;
import Mechanics.Room;

public class EatsShootsAndLeaves extends Hunter {

    private static Direction [] allDirections = { Direction.LEFT, Direction.RIGHT, Direction.BACK, Direction.HERE };
    private static Direction [] movePath = { Direction.LEFT, Direction.RIGHT, Direction.LEFT, Direction.BACK, Direction.RIGHT, Direction.BACK };

    private static int numGames = 0;
    private static int totalLife = 0;

    private static class RoomInfo  {

        public boolean hasWumpus = false;
        public boolean hasLocalHunter = false;
        public boolean hasNeighborHunter = false;
        public boolean hasCorpse = false;
        public boolean hasArrow = false;
        public RoomInfo(Room r) {
            boolean [] status = r.getInfo();
            hasWumpus = status[0];
            hasNeighborHunter = status[1];
            hasCorpse = status[2];
            hasArrow = status[3];
            hasLocalHunter = status[4];
        }

        public String toString() {
            return new String("Status: "
                              + (hasWumpus ? "Wumpus " : "")
                              + (hasNeighborHunter ? "Close Hunter " : "")
                              + (hasCorpse ? "Corpse " : "")
                              + (hasArrow ? "Arrow " : "")
                              + (hasLocalHunter ? "Local Hunter " : ""));
        }
    }

    int turnsAlive = 0;
    int shots = 0, moves = 0;

    public EatsShootsAndLeaves(){
        name = "Eats Shoots and Leaves";
    }

    public void newGame() {

        totalLife += turnsAlive;
        numGames++;

        turnsAlive = shots = moves = 0;
    }

    public void getResponse(boolean[] status){

        turnsAlive++;

        RoomInfo info = new RoomInfo(this.getRoom());
        if (info.hasNeighborHunter || info.hasWumpus) {
            nextMove = Move.SHOOT;
            nextDirection = allDirections[shots++ % 3];
        } else {
            nextMove = Move.MOVE;
            nextDirection = movePath[moves++ % movePath.length];
        }
    }
}
\$\endgroup\$
3
\$\begingroup\$

Idomeneus

Idomeneus is quite simple- if the Wumpus or another hunter is nearby, he fire sarrows everywhere and prays that the goddess of hunting is on his side. If he's near a corpse he lies in wait for the Wumpus. He doesn't like other hunters and will run away if they start firing arrows near him, or if they are in the same room as him. Finally if he's feeling bored he randomly paces the eternal corridors.

package Hunters;
import Mechanics.Direction;
import Mechanics.Hunter;
import Mechanics.Move;
import java.util.Random;



public class Idomeneus extends Hunter
{
    int direction;
    Random r;
    public Idomeneus()
    {
        super();
        name = "Idomeneus";
        direction = 0;
        r = new Random();
    }

    @Override
    public void getResponse(boolean[] status){
        boolean wumpusNear = status[0];
        boolean hunterNear = status[1];
        boolean corpseNear = status[2];
        boolean arrowNear = status[3];
        boolean hunterHere = status[4];
        direction++;

        if(wumpusNear)
        {
            //ATTACK!!!
            nextMove = Move.SHOOT;
            nextDirection = Direction.values()[direction%3];
        }
        else if(hunterHere || arrowNear)
        {
            //Run away
            nextMove = Move.MOVE;
            nextDirection = Direction.values()[r.nextInt(3)];
        }
        else if(hunterNear)
        {
            //ATTACK!!!
            nextMove = Move.SHOOT;
            nextDirection = Direction.values()[direction%3];
        }
        else if(corpseNear)
        {
            //Stay and wait...
            nextMove = Move.MOVE;
            nextDirection = Direction.HERE;
        }
        else
        {
            //wander around
            nextMove = Move.MOVE;
            nextDirection = Direction.values()[r.nextInt(3)];
        }

    }

    public void newGame(){}



}
\$\endgroup\$
  • \$\begingroup\$ Be careful: According to @Cain, you can shoot yourself if you fire into your own room. \$\endgroup\$ – DoctorHeckle Jun 5 '15 at 5:16
3
\$\begingroup\$

Emo Wumpus (Hunter)

Emo Wumpuses (related to Emo Wolves which sometimes use guns and fly planes) hate everything (especially Java). They make no distinction between Hunters and Wumpuses, and try to shoot them all the same. They also hate Nascar drivers, and will always shoot right. If no one is around to shoot, they move right, but will only do this thirteen times in a row before they get even more depressed about being alone and try to shoot themselves (thirteen is an unlucky number). On turn 99, if they are still alive, they will attempt to shoot themselves because starvation is such a lame way to die.

Wumpuses are larger (and heavier) than Wolves, but this one has still starved itself to 424 bytes (as opposed to the hefty 2.72 kb of NascarHunter).

package Hunters;import Mechanics.*;public class EmoWumpus extends Hunter{private int c, t;public EmoWumpus(){name="Emo Wumpus";this.c=0;this.t=0;}public void newGame(){this.c=0;this.t=0;}public void getResponse(boolean[] status){nextMove=Move.SHOOT;if(c<13 && t<100){if(status[0]||status[1]){nextDirection=Direction.RIGHT;}else{nextMove=Move.MOVE;nextDirection=Direction.RIGHT;c++;}}else{nextDirection=Direction.HERE;}t++;}}

Why not just commit suicide in the first place? Emo Wumpuses believe that the one act of good that can do is end the suffering of others before killing themselves. Therefore, they will kill all that they can before it is their time to die.

Wumpus? (Anti-Hunter)

Um, What is a Wumpus doing on the hunters roster? This guy got super pissed that humans were killing his kin, so he decided to dress up as one of them and join in on the hunt. His main goal is to just kill hunters. He tries to hide near corpses, which will give him a chance at a shield from the hunters. If no hunters are nearby, he will move in a direction until hunters are detected, in which case he will try to kill them before moving in the other direction.

Unfortunately, most Wumpuses are stupid, and are still going to try to kill him. Regardless, he considers his sacrifices necessary for the good of Wumpuses everywhere.

package Hunters;

import Mechanics.*;

public class AntiHunter extends Hunter {

private boolean left;

public AntiHunter() {
    name = "Wumpus";
    this.left = true;
}

public void newGame() {
    this.left = true;
}

public void getResponse(boolean[] status) {
    if(status[4]) {
        nextMove = Move.SHOOT;
        nextDirection = Direction.HERE;
    }
    else if(status[2] || status[1]) {
        nextMove = Move.SHOOT;
        if(this.left) {
            this.left = false;
            nextDirection = Direction.LEFT;
        }
        else {
            this.left = true;
            nextDirection = Direction.RIGHT;
        }
    }
    else {
        nextMove = Move.MOVE;
        if(this.left)
            nextDirection = Direction.LEFT;
        else
            nextDirection = Direction.RIGHT;
    }
}

}

\$\endgroup\$
  • 1
    \$\begingroup\$ No longer funny. \$\endgroup\$ – Martin Ender Jun 12 '15 at 14:32
  • \$\begingroup\$ Good thing this isn't code golf. Was wondering how long it'd be until an Emo bot would show up, lol. \$\endgroup\$ – DoctorHeckle Jun 12 '15 at 14:33
  • \$\begingroup\$ @Martin Note that this isn't just suicidal. Out of the bots available for testing, it did not rank last. \$\endgroup\$ – Michael Brandon Morris Jun 12 '15 at 14:39
  • 1
    \$\begingroup\$ EmoSolution is always funny ! \$\endgroup\$ – Mawg Mar 7 '17 at 13:06
2
\$\begingroup\$

Laomedon

Laomedon wanders aimlessly trying to find a corpse. Once he's found one and works out where it is he stays in the same place next to the corpse. When he smells the Wumpus he fires arrows into the Corpse's room.

package Hunters;
import Mechanics.Direction;
import Mechanics.Hunter;
import Mechanics.Move;
public class Laomedon extends Hunter {
    private enum status
    {
        START,
        SEARCHED_LEFT,
        SEARCHED_RIGHT,
        INITIAL_CORPSE_LEFT,
        INITIAL_CORPSE_RIGHT,
        SMELT_CORPSE,
        CORPSE_BEHIND,
        CORPSE_LEFT
    }

    status myState;
    public Laomedon() {
        this.name = "Laomedon";
    }
    @Override
    public void getResponse(boolean[] status) {
        boolean wumpusNear = status[0];
        boolean hunterNear = status[1];
        boolean corpseNear = status[2];
        boolean arrowNear = status[3];
        boolean hunterHere = status[4];
        switch (myState) {
        case CORPSE_BEHIND:
            if(wumpusNear)
            {
                this.nextDirection = Direction.BACK;
                this.nextMove = Move.SHOOT;
            }
            else
            {
                this.nextDirection = Direction.HERE;
                this.nextMove = Move.MOVE;
            }
            break;
        case CORPSE_LEFT:
            if(wumpusNear)
            {
                this.nextDirection = Direction.LEFT;
                this.nextMove = Move.SHOOT;
            }
            else
            {
                this.nextDirection = Direction.HERE;
                this.nextMove = Move.MOVE;
            }
            break;
        case INITIAL_CORPSE_LEFT:
            if(corpseNear)
            {
                this.nextDirection = Direction.RIGHT;
                this.nextMove = Move.MOVE;
                this.myState = Laomedon.status.INITIAL_CORPSE_RIGHT;
            }
            else
            {
                this.nextDirection = Direction.BACK;
                this.nextMove = Move.MOVE;
                this.myState = Laomedon.status.SEARCHED_LEFT;
            }
            break;
        case INITIAL_CORPSE_RIGHT:
            if(corpseNear)
            {
                this.nextDirection = Direction.LEFT;
                this.nextMove = Move.MOVE;
                myState = Laomedon.status.INITIAL_CORPSE_LEFT;
            }
            else
            {
                this.nextDirection = Direction.BACK;
                this.nextMove = Move.MOVE;
                this.myState = Laomedon.status.SEARCHED_LEFT;
            }
            break;
        case SEARCHED_LEFT:
            if(corpseNear)
            {
                this.nextDirection = Direction.LEFT;
                this.nextMove = Move.MOVE;
                this.myState = Laomedon.status.SMELT_CORPSE;
            }
            else
            {
                this.nextDirection = Direction.RIGHT;
                this.nextMove = Move.MOVE;
                this.myState = Laomedon.status.SEARCHED_RIGHT;
            }
            break;
        case SEARCHED_RIGHT:
            if(corpseNear)
            {
                this.nextDirection = Direction.LEFT;
                this.nextMove = Move.MOVE;
                this.myState = Laomedon.status.SMELT_CORPSE;
            }
            else
            {
                this.nextDirection = Direction.LEFT;
                this.nextMove = Move.MOVE;
                this.myState = Laomedon.status.SEARCHED_LEFT;
            }
            break;
        case SMELT_CORPSE:
            if(corpseNear)
            {
                this.nextDirection = Direction.BACK;
                this.nextMove = Move.MOVE;
                this.myState = Laomedon.status.CORPSE_BEHIND;
            }
            else
            {
                this.nextDirection = Direction.BACK;
                this.nextMove = Move.MOVE;
                this.myState = Laomedon.status.CORPSE_LEFT;
            }
            break;
        case START:
            if(corpseNear)
            {
                this.nextDirection = Direction.LEFT;
                this.nextMove = Move.MOVE;
                this.myState = Laomedon.status.INITIAL_CORPSE_LEFT;
            }
            else
            {
                this.nextDirection = Direction.LEFT;
                this.nextMove = Move.MOVE;
                this.myState = Laomedon.status.SEARCHED_LEFT;
            }
            break;
        }
    }

    @Override
    public void newGame() {

        super.newGame();
        myState = status.START;
    }
}

Unfortunately for him, the other hunters don't appreciate his skills and they seem to shoot him a lot...

\$\endgroup\$
2
\$\begingroup\$

NealHunter

After talking about this with my friend DoctorHeckle, I'd thought it would be fun to try it myself. Used the idea for alternating left and right to cover the most area, and then decided to add a bit of responding to the states, but only 0 and 1 - whether or not a Wumpus or hunter are nearby. Doesn't perform as well as NascarHunter, which surprised me at first. After some thinking though, I realized that shooting an arrow in a random direction (like this does) after hearing a hunter/smelling a Wumpus won't do anything if they're moving that turn, since arrows are shot into rooms, but movement is carried out before it kills them. Not quite as effective as I thought... still performs well though!

package Hunters;

import Mechanics.*;
import java.util.Random;

public class NealHunter extends Hunter {

    private boolean goLeft;

    public NealHunter(){
        name = "NealHunter";
        goLeft = false;
    }

    public void newGame() {
        goLeft = false;
    }

    public void getResponse(boolean[] status){

        Random rand = new Random();

        if(status[0] || status[1]){
            nextMove = Move.SHOOT;

            switch ( rand.nextInt(3) ){
                case 0:
                    nextDirection = Direction.LEFT;
                    break;
                case 1:
                    nextDirection = Direction.BACK;
                    break;
                case 2:
                    nextDirection = Direction.RIGHT;
                    break;
            }
        } else {
            nextMove = Move.MOVE;
            if (goLeft) {
                nextDirection = Direction.LEFT;
            } else {
                nextDirection = Direction.RIGHT;
            }

            goLeft = !goLeft;
        }
    }
}
\$\endgroup\$
1
\$\begingroup\$

WalkingIdiot

This one walks until he finds the wumpus. Then, he shoots right. If the wumpus is still there, it must be on the left, so shoot once again. On the way, he doesn't care about other hunters or corpses, hence the name.

package Hunters;

import Mechanics.*;

public class WalkingIdiot extends Hunter {
    private boolean wumpusNear = false;

    @Override
    public void newGame() {
        wumpusNear = false;
    }

    public WalkingIdiot(){
        name = "WalkingIdiot";
    }

    @Override
    public void getResponse(boolean[] status){
        boolean wumpusWasNear = wumpusNear;
        wumpusNear = status[0];
        if (status[0]) {
            nextMove = Move.SHOOT;
            if (wumpusWasNear) {
                nextDirection = Direction.LEFT;
            } else {
                nextDirection = Direction.RIGHT;
            }
            return;
        }
        nextMove = Move.MOVE;
        nextDirection = Math.random() < 0.5 ? Direction.LEFT : Direction.RIGHT;
    }
}

Stay

Stay doesn't like to walk. It simply shoots around and remembers if he shot a hunter.

package Hunters;

import Mechanics.*;

public class Stay extends Hunter {
    private Direction lastShot = Direction.LEFT;
    private Direction corpse = null;
    private boolean hunterNear = false;

    public Stay(){
        name = "Stay";
    }

    @Override
    public void newGame() {
        corpse = null;
        hunterNear = false;
        lastShot = Direction.LEFT;
    }

    @Override
    public void getResponse(boolean[] status){
        nextMove = Move.SHOOT;//always
        boolean hunterWasNear = hunterNear;
        hunterNear = status[1];

        if (hunterWasNear && status[2] && !status[1]) {
            corpse = lastShot;
        }

        if (status[0]) {
            if (corpse != null) {
                nextDirection = corpse;
                return;
            }
        }
        if ((status[1] && !status[4]) || status[0]) {
            switch (lastShot) {
                case LEFT: lastShot = nextDirection = Direction.RIGHT; break;
                case RIGHT: lastShot = nextDirection = Direction.BACK; break;
                case BACK: lastShot = nextDirection = Direction.LEFT; break;
            }
            return;
        }

        //default
        lastShot = nextDirection = Direction.LEFT;
    }
}
\$\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.