Save the last bullet for yourself

51

14

The contest is now over. Coward is the winner. You can watch the last match here.

Suddenly zombies appear! OH NOES!

In this king-of-the-hill challenge, you must create a bot to survive the zombie apocalypse. Or at least, hold out for as long as possible.

At the start of the game, 50 instances of each entry will be placed randomly in a large toroidal play area - that is, it appears to be square, but wraps around. The size of the play area will vary depending on the number of entries, but initially 6% of squares will be occupied. Each competitor starts with 3 bullets.

At the beginning of each turn, a zombie will rise from the ground at a random location, destroying whatever was above it. Any player who is next to a zombie at the start of their turn will become a zombie.

For each living player, their code will then be called. It will receive a PlayerContext object, containing information on their current status, and their surroundings. Each player can see for 8 squares in any direction.

The player must choose to either move (staying still is a valid movement), by returning a Move, or shoot a nearby person or zombie, by returning a Shoot. Your gun has a maximum range of 5 squares. Since you are within your gun's range you can shoot yourself, provided you have bullets left. If two players shoot each other, they both die.

If two players attempt to move onto the same square, they will fail, and will both return to the square they started from. If there are still conflicts, this will be repeated until there are no conflicts, which may mean everyone is back where they started.

If a player dies from a gunshot, their dead body will remain, and forms a permanent barrier. Any bullets they were carrying remain on their person, and can be scavenged by players in adjacent squares. If there are multiple players occupying the squares adjacent to a dead body, then the bullets will be shared between them, but any remainder will be lost.

If a player becomes a zombie, then their bullets are lost. Zombies will mindlessly walk towards the nearest living player.

Entries are scored on how long their longest-surviving player survives.

Entries

A control program is available at https://github.com/jamespic/zombies. Simply clone it, and run mvn compile exec:java.

To be eligible, entries must be written in a JVM language, must be portable, and must be possible to build from Maven with no special set-up. This is to ensure that competitors do not need to install multiple run-time environments to test their bots against competitors.

Sample entries are currently available in the following languages:

If you would like to compete in a language that is not listed, you can post a comment requesting it, and I will investigate the possibility of integrating your chosen language into the control program. Or, if you are impatient, you can submit a pull request to the control program.

Only one instance (in the Java sense of the word) will be created for each entry. This Java instance will be called multiple times per turn - once for each surviving player.

API

package zombie

// You implement this. Your entry should be in package `player`
interface Player {
    Action doTurn(PlayerContext context)
}

// These already exist
class PlayerContext {
    // A square array, showing the area around you, with you at the centre
    // playFields is indexed by x from West to East, then y from North to South
    PlayerId[][] getPlayField()
    int getBullets() // Current bullets available
    int getGameClock() // Current turn number
    PlayerId getId() // Id of the current player instance
    int getX() // Your current x co-ordinate
    int getY() // Your current y co-ordinate
    int getBoardSize() // The size of the current playing field
    Set<PlayerId> shootablePlayers() // A helper function that identifies players in range.
}

class PlayerId {
    String getName() // The name of the entrant that owns this player
    int getNumber() // A unique number, assigned to this player
}

// Don't implement this. Use either `Move` or `Shoot`
interface Action {}

enum Move implements Action {
    NORTHWEST, NORTH, NORTHEAST,
    EAST, STAY, WEST,
    SOUTHEAST, SOUTH, SOUTHWEST;
    static move randomMove();
}

class Shoot implements Action {
    Shoot(PlayerId target);
}

Additional Rules

Each entry must have a unique name, in order to work correctly with the control program.

Entries should not attempt to tamper with other entrants, or with the control program, or otherwise take advantage of the run-time environment to "break the fourth wall", and gain an advantage that would not be available in a "real" zombie apocalypse.

Communication between players is allowed.

The winner is the entrant whose bot has the highest score in a test I will run on the 3rd of August 2014.

Final Results

The final results are in! Coward is the winner!

On 2nd August, I ran 19 rounds of the control program, and ranked each player according to their median score. The results were as follows:

Coward: 4298
Fox: 3214
Shotguneer: 2471
Cocoon: 1834
JohnNash: 1240
HuddleWolf: 1112
Sokie: 1090
SOS: 859
GordonFreeman: 657
Jack: 657
Waller: 366
SuperCoward: 269
MoveRandomly: 259
StandStill: 230
Vortigaunt: 226
ThePriest: 223
Bee: 61
HideyTwitchy: 52
ZombieHater: 31
Gunner: 20
ZombieRightsActivist: 16
SunTzu: 11
EmoWolfWithAGun: 0

The last round is available to watch here.

Run-by-run results

The individual results of each of the 19 runs were:

#Run at 03-Aug-2014 14:45:35#
Bee: 21
Cocoon: 899
Coward: 4608
EmoWolfWithAGun: 0
Fox: 3993
GordonFreeman: 582
Gunner: 18
HideyTwitchy: 37
HuddleWolf: 2836
Jack: 839
JohnNash: 956
MoveRandomly: 310
SOS: 842
Shotguneer: 2943
Sokie: 937
StandStill: 250
SunTzu: 3
SuperCoward: 318
ThePriest: 224
Vortigaunt: 226
Waller: 258
ZombieHater: 41
ZombieRightsActivist: 10

#Run at 03-Aug-2014 14:56:48#
Bee: 97
Cocoon: 3073
Coward: 5699
EmoWolfWithAGun: 0
Fox: 4305
GordonFreeman: 1252
Gunner: 24
HideyTwitchy: 25
HuddleWolf: 3192
Jack: 83
JohnNash: 1195
MoveRandomly: 219
SOS: 884
Shotguneer: 3751
Sokie: 1234
StandStill: 194
SunTzu: 69
SuperCoward: 277
ThePriest: 884
Vortigaunt: 564
Waller: 1281
ZombieHater: 10
ZombieRightsActivist: 2

#Run at 03-Aug-2014 15:01:37#
Bee: 39
Cocoon: 2512
Coward: 2526
EmoWolfWithAGun: 0
Fox: 2687
GordonFreeman: 852
Gunner: 21
HideyTwitchy: 91
HuddleWolf: 1112
Jack: 1657
JohnNash: 944
MoveRandomly: 312
SOS: 660
Shotguneer: 1067
Sokie: 1356
StandStill: 169
SunTzu: 8
SuperCoward: 351
ThePriest: 223
Vortigaunt: 341
Waller: 166
ZombieHater: 25
ZombieRightsActivist: 47

#Run at 03-Aug-2014 15:08:27#
Bee: 27
Cocoon: 2026
Coward: 3278
EmoWolfWithAGun: 0
Fox: 2677
GordonFreeman: 611
Gunner: 16
HideyTwitchy: 11
HuddleWolf: 1694
Jack: 600
JohnNash: 1194
MoveRandomly: 48
SOS: 751
Shotguneer: 5907
Sokie: 1233
StandStill: 62
SunTzu: 9
SuperCoward: 252
ThePriest: 173
Vortigaunt: 107
Waller: 276
ZombieHater: 53
ZombieRightsActivist: 38

#Run at 03-Aug-2014 15:14:01#
Bee: 26
Cocoon: 1371
Coward: 5121
EmoWolfWithAGun: 0
Fox: 3878
GordonFreeman: 464
Gunner: 29
HideyTwitchy: 130
HuddleWolf: 955
Jack: 101
JohnNash: 698
MoveRandomly: 269
SOS: 1314
Shotguneer: 2444
Sokie: 3217
StandStill: 233
SunTzu: 10
SuperCoward: 269
ThePriest: 318
Vortigaunt: 266
Waller: 494
ZombieHater: 49
ZombieRightsActivist: 9

#Run at 03-Aug-2014 15:19:43#
Bee: 25
Cocoon: 2098
Coward: 4855
EmoWolfWithAGun: 0
Fox: 4081
GordonFreeman: 227
Gunner: 43
HideyTwitchy: 28
HuddleWolf: 2149
Jack: 1887
JohnNash: 1457
MoveRandomly: 117
SOS: 1068
Shotguneer: 4272
Sokie: 636
StandStill: 53
SunTzu: 9
SuperCoward: 209
ThePriest: 220
Vortigaunt: 227
Waller: 366
ZombieHater: 19
ZombieRightsActivist: 49

#Run at 03-Aug-2014 15:24:03#
Bee: 46
Cocoon: 682
Coward: 3588
EmoWolfWithAGun: 0
Fox: 4169
GordonFreeman: 764
Gunner: 13
HideyTwitchy: 21
HuddleWolf: 842
Jack: 1720
JohnNash: 1260
MoveRandomly: 259
SOS: 636
Shotguneer: 777
Sokie: 586
StandStill: 75
SunTzu: 6
SuperCoward: 390
ThePriest: 189
Vortigaunt: 208
Waller: 334
ZombieHater: 61
ZombieRightsActivist: 20

#Run at 03-Aug-2014 15:29:49#
Bee: 90
Cocoon: 516
Coward: 4298
EmoWolfWithAGun: 0
Fox: 1076
GordonFreeman: 581
Gunner: 8
HideyTwitchy: 87
HuddleWolf: 4298
Jack: 4715
JohnNash: 727
MoveRandomly: 102
SOS: 859
Shotguneer: 2471
Sokie: 2471
StandStill: 427
SunTzu: 24
SuperCoward: 159
ThePriest: 359
Vortigaunt: 94
Waller: 398
ZombieHater: 54
ZombieRightsActivist: 21

#Run at 03-Aug-2014 15:36:50#
Bee: 18
Cocoon: 3127
Coward: 3124
EmoWolfWithAGun: 0
Fox: 5094
GordonFreeman: 255
Gunner: 43
HideyTwitchy: 17
HuddleWolf: 1078
Jack: 272
JohnNash: 1270
MoveRandomly: 55
SOS: 723
Shotguneer: 3126
Sokie: 1388
StandStill: 179
SunTzu: 7
SuperCoward: 45
ThePriest: 519
Vortigaunt: 172
Waller: 200
ZombieHater: 45
ZombieRightsActivist: 8

#Run at 03-Aug-2014 15:40:59#
Bee: 78
Cocoon: 1834
Coward: 4521
EmoWolfWithAGun: 0
Fox: 1852
GordonFreeman: 657
Gunner: 7
HideyTwitchy: 2
HuddleWolf: 969
Jack: 895
JohnNash: 1596
MoveRandomly: 277
SOS: 694
Shotguneer: 1397
Sokie: 844
StandStill: 325
SunTzu: 7
SuperCoward: 192
ThePriest: 148
Vortigaunt: 369
Waller: 232
ZombieHater: 16
ZombieRightsActivist: 17

#Run at 03-Aug-2014 15:44:22#
Bee: 23
Cocoon: 2638
Coward: 2269
EmoWolfWithAGun: 0
Fox: 2067
GordonFreeman: 730
Gunner: 21
HideyTwitchy: 60
HuddleWolf: 763
Jack: 1469
JohnNash: 1494
MoveRandomly: 273
SOS: 3181
Shotguneer: 3181
Sokie: 653
StandStill: 450
SunTzu: 19
SuperCoward: 272
ThePriest: 215
Vortigaunt: 299
Waller: 510
ZombieHater: 62
ZombieRightsActivist: 16

#Run at 03-Aug-2014 15:48:03#
Bee: 97
Cocoon: 2009
Coward: 2798
EmoWolfWithAGun: 0
Fox: 1907
GordonFreeman: 958
Gunner: 22
HideyTwitchy: 93
HuddleWolf: 925
Jack: 288
JohnNash: 476
MoveRandomly: 422
SOS: 3723
Shotguneer: 2076
Sokie: 1090
StandStill: 134
SunTzu: 92
SuperCoward: 141
ThePriest: 470
Vortigaunt: 216
Waller: 340
ZombieHater: 32
ZombieRightsActivist: 20

#Run at 03-Aug-2014 16:03:38#
Bee: 121
Cocoon: 501
Coward: 9704
EmoWolfWithAGun: 0
Fox: 3592
GordonFreeman: 588
Gunner: 20
HideyTwitchy: 54
HuddleWolf: 749
Jack: 1245
JohnNash: 1345
MoveRandomly: 451
SOS: 835
Shotguneer: 1548
Sokie: 589
StandStill: 166
SunTzu: 11
SuperCoward: 158
ThePriest: 93
Vortigaunt: 246
Waller: 1350
ZombieHater: 18
ZombieRightsActivist: 11

#Run at 03-Aug-2014 16:10:24#
Bee: 66
Cocoon: 1809
Coward: 3295
EmoWolfWithAGun: 0
Fox: 3214
GordonFreeman: 1182
Gunner: 15
HideyTwitchy: 52
HuddleWolf: 1514
Jack: 101
JohnNash: 745
MoveRandomly: 211
SOS: 862
Shotguneer: 6335
Sokie: 1504
StandStill: 384
SunTzu: 14
SuperCoward: 259
ThePriest: 244
Vortigaunt: 262
Waller: 1356
ZombieHater: 24
ZombieRightsActivist: 20

#Run at 03-Aug-2014 16:28:05#
Bee: 61
Cocoon: 692
Coward: 11141
EmoWolfWithAGun: 0
Fox: 1955
GordonFreeman: 1234
Gunner: 42
HideyTwitchy: 24
HuddleWolf: 1862
Jack: 609
JohnNash: 1579
MoveRandomly: 167
SOS: 958
Shotguneer: 11141
Sokie: 284
StandStill: 422
SunTzu: 66
SuperCoward: 121
ThePriest: 207
Vortigaunt: 128
Waller: 259
ZombieHater: 22
ZombieRightsActivist: 7

#Run at 03-Aug-2014 16:32:10#
Bee: 207
Cocoon: 4414
Coward: 2670
EmoWolfWithAGun: 0
Fox: 978
GordonFreeman: 620
Gunner: 19
HideyTwitchy: 135
HuddleWolf: 962
Jack: 657
JohnNash: 1200
MoveRandomly: 147
SOS: 687
Shotguneer: 2258
Sokie: 2433
StandStill: 249
SunTzu: 49
SuperCoward: 1056
ThePriest: 602
Vortigaunt: 326
Waller: 593
ZombieHater: 31
ZombieRightsActivist: 10

#Run at 03-Aug-2014 16:38:56#
Bee: 265
Cocoon: 2231
Coward: 4228
EmoWolfWithAGun: 0
Fox: 4737
GordonFreeman: 532
Gunner: 9
HideyTwitchy: 75
HuddleWolf: 2375
Jack: 1237
JohnNash: 1249
MoveRandomly: 109
SOS: 860
Shotguneer: 6470
Sokie: 1096
StandStill: 126
SunTzu: 15
SuperCoward: 393
ThePriest: 133
Vortigaunt: 184
Waller: 257
ZombieHater: 32
ZombieRightsActivist: 12

#Run at 03-Aug-2014 16:52:16#
Bee: 67
Cocoon: 1534
Coward: 9324
EmoWolfWithAGun: 0
Fox: 2458
GordonFreeman: 1019
Gunner: 24
HideyTwitchy: 72
HuddleWolf: 601
Jack: 399
JohnNash: 1366
MoveRandomly: 275
SOS: 506
Shotguneer: 1007
Sokie: 475
StandStill: 230
SunTzu: 135
SuperCoward: 361
ThePriest: 61
Vortigaunt: 112
Waller: 4106
ZombieHater: 12
ZombieRightsActivist: 22

#Run at 03-Aug-2014 17:03:04#
Bee: 26
Cocoon: 1159
Coward: 7796
EmoWolfWithAGun: 0
Fox: 3948
GordonFreeman: 878
Gunner: 3
HideyTwitchy: 17
HuddleWolf: 1490
Jack: 513
JohnNash: 1240
MoveRandomly: 487
SOS: 1460
Shotguneer: 1481
Sokie: 832
StandStill: 457
SunTzu: 8
SuperCoward: 480
ThePriest: 527
Vortigaunt: 171
Waller: 3729
ZombieHater: 30
ZombieRightsActivist: 10

James_pic

Posted 2014-07-20T19:48:18.813

Reputation: 3 988

As far as scavenging bullets goes: if you just move next to the body, you automatically do it (as opposed to needing to do it as an action), correct? – Foon – 2014-07-21T21:08:34.763

@Foon That is correct. You don't need to do anything special. You'll have them automatically on the next turn. – James_pic – 2014-07-21T21:17:06.813

I can't clone the repo or find the frege adapter package online. Could someone direct me? – Pureferret – 2014-07-25T10:25:18.850

1

@Pureferret The frege code consist of a Frege file containing bindings at https://github.com/jamespic/zombies/blob/master/src/main/frege-bindings/zombie.fr, and a Java helper class that calls into Frege at https://github.com/jamespic/zombies/blob/master/src/main/java/zombie/FregeAdapter.java. If you can clone the repo (or download it as a zip from https://github.com/jamespic/zombies/archive/master.zip) then Maven will handle the build for you.

– James_pic – 2014-07-25T11:16:17.983

@Pureferret If you're implementing an entry in Frege, then you'll need to create a module within the package player containing a doTurn function, that takes a PlayerContext and returns either an Action, an IO Action, or a Continue (Continue is a type defined in the frege bindings, that contains a result and a continuation). – James_pic – 2014-07-25T11:26:40.907

@James_pic I dont care if I get much rep on this site (i'm re-learning java so am trying to get practice). I finished signing up though so I can put a bounty on this to get it more attention for the Aug 3 results. Any objections? – kaine – 2014-07-25T18:30:23.103

@kaine I don't mind if you want to start a bounty (I was going to start one myself), but bear in mind that bounties can only run for 7 days, so if the bounty is for the winner, then there's no sense in starting it before the 27th. I've also been considering a bounty for the best entry in a language other than Java, but a plain bounty for the winner also makes sense. – James_pic – 2014-07-25T18:35:15.990

@James_pic I agree, I'll post one on Monday for the max rep I can. IF I win (likely by posting one other than Shotguneer) I will give it to 2nd place (don't think I can give bounty to myself and no point not awarding it). – kaine – 2014-07-25T18:38:17.787

When you say "Communication between players is allowed" does that mean I can add my own public methods to my player class that I can access? i.e. PlayerId player; player.dontMove() – Moop – 2014-07-25T21:12:04.470

@Moop You can add whatever you like to your player class (I.e, your implementation of Player), but you can't modify classes that are part of the runtime environment – James_pic – 2014-07-25T21:16:42.783

Is there a way to tell if the player got killed, and if so how? – Moop – 2014-07-25T21:21:25.937

@Moop not directly, but since PlayerContext includes the turn number, you could keep track of when you last heard from each player, and if you haven't heard from them in a couple of turns, they must have been killed or turned. – James_pic – 2014-07-25T22:13:12.197

@James I've not got maven to work in the past so I'm just manually trying to set up the project. I'll add those too the build party next. By "Can't modify classes that are part of the run time environment" I guess that means no reflection? – Pureferret – 2014-07-25T23:47:01.497

1@Pureferret Trying to set up the project manually will be painful. No-one's really used them yet, but there are compilers and interpreters for half a dozen languages. Reflection's allowed (indeed, some of the dynamic languages can't work without it), so long as it's not used to tamper with the game or your competitors. Look at Coward's loot tracking for an example of "communication". – James_pic – 2014-07-26T07:38:12.427

@James it's mostly set up. I was wondering if checking for enemies number of bullets is OK? – Pureferret – 2014-07-26T08:11:11.510

@Pureferret No, I'm afraid checking enemies for bullets wouldn't be OK. I'm also not even sure how you'd do it - I don't think the player ever receives anything that even holds a private reference that leads to that information. – James_pic – 2014-07-26T08:40:18.197

Hey I was wondering something about "PLayers can communicate" Can I have something like: public static Map<Point,MyPlayer> myPack = new HashMap<>(); That I set on every turn? I think that breaks the 4th wall? – sokie – 2014-07-28T15:38:02.570

1@sokie That sounds OK - some entries already do something like this, and I figured when I set the challenge that some entrants would want their bots to rendez-vous somewhere, or exchange information about their surroundings. We'll say that the players have got walkie talkies. – James_pic – 2014-07-28T15:43:24.060

Could you clear the game_ouput folder of .html every time you run the program? – Moop – 2014-07-28T17:42:47.450

@Moop Maybe. I suspect it's a job for Maven, as Java's file APIs tend to make things harder than they should be, but I'll look into it. Or, feel free to submit a pull request on the control program. – James_pic – 2014-07-28T18:04:46.720

@Moop I've merged your changes. Cheers for the help – James_pic – 2014-07-28T19:27:00.983

Scavenging bullets: what if there are 3 bullets and 4 adjacent players; who gets left out? – JoshDM – 2014-07-29T20:07:26.823

@JoshDM if there's a remainder when dividing the number of bullets by the number of players, then the remainder is lost (presumably due to the players fighting over them). So in your case, nobody would get any bullets. – James_pic – 2014-07-29T20:14:26.460

@James_pic - ah, right. That makes sense. I was thinking a remainder if 3 bullets over 2 players. – JoshDM – 2014-07-29T20:20:40.953

It seems like shootablePlayers shouldn't include the player itself by default. Maybe add if(x == CENTRE_OF_VISION && y == CENTRE_OF_VISION) continue; ? – Moop – 2014-07-29T22:35:22.210

Also, are we allow to know the board size so we can optimize movement over the torus? – Moop – 2014-07-29T23:30:51.017

@Moop shootablePlayers was originally added for internal use - to check whether the player you're trying to shoot is actually in range. I left it in the public API more for convenience than anything else. Changing the implementation to exclude the current player would break this use case. – James_pic – 2014-07-30T06:15:52.780

@Moop It seems reasonable to want to know the board size (it just didn't occur to me to add it to the API), so I'll add that in when I get chance. However, I probably won't get chance until at least 20:00 UTC, so if you're in a hurry, I'm happy if you want to make the change yourself and pull-request it in. – James_pic – 2014-07-30T06:20:06.420

I've updated it so you can now access boardSize from PlayerContext – James_pic – 2014-07-30T11:28:19.957

@James_pic Thanks James for the quick update! – Moop – 2014-07-30T13:30:32.410

2

@James_pic I added this code to my local copy (http://pastebin.com/PutPn9ff) in Game.java so I could use the arrow keys to go forward and backwards in the game. Thought it might be useful for you to add

– Moop – 2014-07-30T19:39:07.110

@Moop Neat! I'll include it in the next test run, which should be coming up shortly – James_pic – 2014-07-30T20:39:11.137

I am always surprised how well StandStill does. I made a local player that just randomly moves each turn and it does horrible. – Moop – 2014-07-30T21:32:10.883

@Moop Part of the reason may be that some players are aggressive towards players they don't know, until they figure out if they can trust them - other non-aggressive players like Priest and Vortigaunt also seem to benefit from this. StandStill may also sometimes benefit from the "three stooge effect" that you can get when multiple zombies are approaching side-by-side. – James_pic – 2014-07-30T22:20:04.523

@James_pic You can save a lot of space in the html output by using css to style. Try adding this to the game.java: http://pastebin.com/KnaZxqkS

– Moop – 2014-08-01T14:28:17.400

I would be awesome if somebody could create a video of the final result! – Martin Thoma – 2014-08-01T21:33:19.333

Here is a list of resources: Where can I find free sprites and images?

– Martin Thoma – 2014-08-01T21:35:53.733

Is there a way to see who shot my character (within the rules?) – Tyzoid – 2014-08-01T23:05:17.727

@Tyzoid No, there isn't. This information isn't currently tracked anywhere, or exposed in any API. Or, in-universe, dead men don't tell tales. – James_pic – 2014-08-01T23:08:20.893

@Moop Messing with the CSS got the size down by about 70%, which puts the last run at around 400MB. That should be small enough to host on GitHub pages, but I'm struggling to get it to work. I might just stick it in an S3 bucket. – James_pic – 2014-08-01T23:09:54.557

@James_pic also: how slow can my algorithm be? It's slowing it down noticeably, but it's not terrible. – Tyzoid – 2014-08-01T23:53:30.753

1

@moose http://opengameart.org/content/characters-zombies-and-weapons-oh-my looks like it could be a good asset set to use. But I'm not sure I'll have time to put something like this together.

– James_pic – 2014-08-02T00:13:52.437

@Tyzoid I'm already regretting some of the slower entries (some of the slowest were mine - I had to be stubborn and implement them in Frege and Javascript), as it currently takes around 10 minutes to run a round on my laptop. I'd say that "not terrible" is probably just about reasonable. But I'm probably not going to wait an hour to run a single round, if it comes to that. – James_pic – 2014-08-02T00:18:31.503

@James_pic An hour? I can get a round down in under a minute. Do you have a SSD? – Moop – 2014-08-02T03:15:49.397

Would anyone mind if I got a code review on my entry, I don't think it's working 'right'? I'm also struggling to debug it, is there a way to compile the code and then just run it in debug mode without having to compile it each time? I also find as the Compile and execute is separat, debugging and tweaking with 'hot insert' (right term?) is impossible :(

– Pureferret – 2014-08-02T07:05:38.807

@Moop My laptop's a two or three year old Core i3. I expect it'd run faster on newer hardware (a minute sounds about right), since profiling shows most of that time is either running entries (last time I checked, Cocoon was the slowest, since it uses a slow algorithm in a slow language), or assembling the PlayerContext before a competitor's turn. I expect a fast CPU would make more difference than an SSD, as this is all CPU time. – James_pic – 2014-08-02T10:25:05.530

@Pureferret Posting it for code review sounds fine to me. The easiest way to debug your code is probably to run it in an IDE. The better IDEs have Maven integration, so they should be able to build the control program without any help. They also support hot-swapping, so it's possible to debug the program and edit the code on-the-fly (google "hot swap <the name of your IDE>" for help on how to do that in your IDE). These days, I'd recommend IntelliJ IDEA, or else NetBeans, or else Eclipse. – James_pic – 2014-08-02T10:35:53.503

@James_pic I did run it in the IDE, I have eclipse set up nicely at home (but not work) and I run it as compile exec:java like you suggest, but that means (AFAICT) that it's fully compiled before runtime so I can't hotswap. I'll look up hot swapping with maven specifically though. – Pureferret – 2014-08-02T14:32:17.240

@Pureferret There is a plugin for eclipse that provides Maven integration (the fact that it doesn't come as standard is one of the reasons Eclipse is at the bottom of my list). In any case, all mvn exec:java does is execute player.Game as a main class, which you can do directly in Eclipse. I reckon there's a reasonable chance hot-swapping will work when run in this way. – James_pic – 2014-08-02T15:00:10.047

1@James_pic Thanks for the post and game, I enjoyed it. – Moop – 2014-08-03T18:15:37.053

2Slight disappointment that the winner let itself suffer certain zombification rather than committing suicide. – Sparr – 2014-08-04T03:06:09.387

1@Sparr The best entries generally got themselves into a "fort", so they could only be killed if a zombie spawned right beneath their feet. In that case, there's no warning that zombies are approaching. – James_pic – 2014-08-04T09:25:22.860

1@James_pic the actual winner ended up in a "big" fort, with two copies of himself, so he had warning of zombies spawning nearby in his fort, and he had one turn of warning on the very last turn when he knew he would die and he chose to shoot one of the two zombies instead of shooting himself. Very contrary to the title of the contest :| – Sparr – 2014-08-04T14:16:32.847

@sparr youre right, i didnt plan for this. its really unusual to have this chance at all (since an adjacent zombie kills you at the start of your turn, except that zombie is another player that got zombified in the same turn). In game, coward Steve shot his friend coward Allen who got zombified before his very eyes and spared him that horrific afterlife. But unfortunately for Steve ... – Thaylon – 2014-08-04T22:18:53.657

If somebody made a video, we could put it on http://www.twitch.tv/

– Martin Thoma – 2014-08-08T03:08:20.580

Answers

16

Coward

The rules of cowardice.

  1. If you cant run away, panic and shoot everything you don't know.
  2. Run!!!
  3. When running, you might aswell pick up some bullets. Deep down you know you cant run forever.
  4. When running, seek other cowards. Misery loves company. And they might eat the other guy first.
package player;

import java.lang.Math.*;
import java.util.Set;
import java.util.HashSet;
import zombie.*;
import static zombie.Constants.*;

public class Coward implements Player {

    private static final Set<PlayerId> killed = new HashSet<>();
    private static final Set<PlayerId> looted = new HashSet<>();

    @Override
    public Action doTurn(PlayerContext context) {

        PlayerId[][] field = context.getPlayField();

        // Panic and shoot
        if (context.getBullets() > 0) {
            int distEnemy = VISION_WIDTH;
            int distZombie = VISION_WIDTH;
            PlayerId targetEnemy = null;
            PlayerId targetZombie = null;
            for (int x = CENTRE_OF_VISION - SHOOT_RANGE; x <= CENTRE_OF_VISION + SHOOT_RANGE; x++) {
                for (int y = CENTRE_OF_VISION - SHOOT_RANGE; y <= CENTRE_OF_VISION + SHOOT_RANGE; y++) {
                    PlayerId player = field[x][y];
                    if (player != null && !killed.contains(player)) {
                        int dist = getDistance(x, y);
                        if (player.getName().equals("Zombie")) {
                            if( dist < distZombie ) {
                                distZombie = dist;
                                targetZombie = player;
                            }
                        } else if (isEnemy(player.getName()) && dist <= distEnemy ) {
                            distEnemy = dist;
                            targetEnemy = field[x][y];
                        }
                    }
                }
            }

            if (targetZombie != null && distZombie <= 3) {
                killed.add(targetZombie);
                return new Shoot( targetZombie );
            } else if (targetEnemy != null && distEnemy <= 5 ) {
                killed.add(targetEnemy);
                return new Shoot( targetEnemy );
            }
        }

        // Looted?
        for( int xx = CENTRE_OF_VISION-VISION_RANGE+1; xx <= CENTRE_OF_VISION+VISION_RANGE-1; xx++ ) {
            for( int yy = CENTRE_OF_VISION-VISION_RANGE+1; yy <= CENTRE_OF_VISION+VISION_RANGE-1; yy++ ) {
                PlayerId player = field[xx][yy];
                if( player != null && !player.getName().equals("Zombie") && !player.getName().equals("DeadBody")) {
                    for( int x = -1; x <= 1; x++ ) {
                        for( int y = -1; y <= 1; y++ ) {
                            PlayerId loot = field[xx+x][yy+y];
                            if( loot != null && !looted.contains(loot) && loot.getName().equals("DeadBody")) {
                                looted.add(loot);
                            }
                        }
                    }
                }
            }
        }

        // Run away
        int bestScore = -10000000;
        Move bestMove = Move.randomMove();

        for( int x = -1; x <= 1; x++ ) {
            for( int y = -1; y <= 1; y++ ) {
                PlayerId center = field[CENTRE_OF_VISION+x][CENTRE_OF_VISION+y];
                if( center == null ) {
                    int thisScore = 0;
                    for( int xx = CENTRE_OF_VISION+x-VISION_RANGE+1; xx < CENTRE_OF_VISION+x+VISION_RANGE; xx++ ) {
                        for( int yy = CENTRE_OF_VISION+y-VISION_RANGE+1; yy < CENTRE_OF_VISION+y+VISION_RANGE; yy++ ) {
                            PlayerId player = field[xx][yy];
                            if( player != null) {
                                int dist = getDistance(xx-x,yy-y);

                                if( player.getName().equals("Coward")) { // Prefer lose groups
                                    thisScore += (int)Math.pow( 2, ( 6 - Math.abs( dist - 5 )));
//                                    if( dist >= 3 && dist <= 6 ) {
//                                        thisScore += 32;
//                                    } else if( dist > 3 ) {
//                                        thisScore += 16;
//                                    }
                                } else if( player.getName().equals("DeadBody")) { // Visit dead bodies on the route
                                    if( !looted.contains(player)) {
                                        thisScore += (32+VISION_RANGE-dist)*(VISION_RANGE-dist);
                                    }
                                } else if( player.getName().equals("Zombie")) { // Avoid zombies
                                    if( dist <= 5 ) {
                                        thisScore -= (int)Math.pow( 10, ( 6 - dist ));
                                    }
//                                    if( dist <= 2 ) {
//                                        thisScore -= 10000;
//                                    } else if( dist <= 3 ) {
//                                        thisScore -= 1000;
//                                    } else if( dist <= 4 ) {
//                                        thisScore -= 100;
//                                    }
                                } else if( isEnemy(player.getName())) { // Avoid strangers
                                    thisScore -= (int)Math.pow( 10, ( 9 - dist ));
//                                    if( dist == 7 ) {
//                                        thisScore -= 100;
//                                    } else if( dist <= 6 ) {
//                                        thisScore -= 1000;
//                                    }
                                }
                            }
                        }
                    }
                    if( thisScore > bestScore ) {
                        bestScore = thisScore;
                        bestMove = Move.inDirection( x, y );
                    }
                }
            }
        }

        return bestMove;
    }

    private boolean isEnemy(String name) {
        switch (name) {
            case "Coward":
            case "DeadBody":
            case "GordonFreeman":
            case "EmoWolfWithAGun":
            case "HuddleWolf":
            case "ThePriest":
            case "Shotguneer":
            case "Vortigaunt":
            case "Fox":
            case "Cocoon":
            case "SuperCoward":
            case "SOS":
            case "JohnNash":
            case "MoveRandomly":
                return false;
            default:
                return true;
        }
    }

    private int getDistance(int x, int y) {
        return Math.max(Math.abs(CENTRE_OF_VISION - x), Math.abs(CENTRE_OF_VISION - y));
    }
}

Thaylon

Posted 2014-07-20T19:48:18.813

Reputation: 1 324

I'm excited to try this out. All the entries so far have focussed on fighting, but no-one's tried running or hiding. – James_pic – 2014-07-24T14:11:31.947

Oh, and you don't need to keep track of what you've killed. It'll show up as a DeadBody with a different id, rather than whatever it was before. – James_pic – 2014-07-24T14:14:09.300

1Its not for keeping track, its for not shooting the same thing twice in same round. – Thaylon – 2014-07-24T14:16:22.273

Ah, Smart! Save bullets – James_pic – 2014-07-24T14:17:54.583

I hope you don't mind, but I've fixed a couple of issues that caused it to not compile, or to crash at run-time (mostly missing imports and off-by-n errors). I'll post updated results when I get home. – James_pic – 2014-07-24T14:51:56.393

I appreciate the fixes :) – Thaylon – 2014-07-24T14:53:50.703

1

Made several changes, best run with an epic showdown was at 2681. https://dl.dropboxusercontent.com/u/13918324/2681.html

– Thaylon – 2014-07-24T21:57:40.003

Neat! I can't wait to try it out. I see you've spotted the "three stooges problem" with the zombie movement - if two or more try to move to the same space, they block each other. I only spotted it after the competition started. I'd considered randomising the zombies movement slightly, to break the deadlock, but figured it was unfair on existing entries. – James_pic – 2014-07-25T07:21:05.307

1

@Thaylon Formatted the latest sourcecode for you.

http://pastebin.com/4WDb6s8C

– HuddleWolf – 2014-07-25T13:15:57.833

@HuddleWolf Thanks! – Thaylon – 2014-07-25T13:22:42.247

Removed Shotguneer from friendlies because it now tries to kill all other players closeby. – Thaylon – 2014-07-27T00:54:14.340

Hey Thaylon, I've copied your // Panic and shoot bit, since I needed it, hope you don't mind. I'll rewrite it otherwise. – sokie – 2014-07-29T13:03:30.560

@sokie It's alright with me. Let me know if you want me to add sokie as friendly to fox and/or coward. – Thaylon – 2014-07-29T13:11:48.890

@Thaylon you can probably add SuperCoward, but sokie actually fights. Atm sokie in my tests places second most of the times, I'll try to see what happens if I add coward to friendlies and change your coward as well! I'll let you know – sokie – 2014-07-29T13:13:42.420

44

Emo Wolf With A Gun

He's back. He hates zombies. He still hates Java. No copyright infringement intended.

package player;

import zombie.*;

public class EmoWolfWithAGun implements Player {

    @Override
    public Action doTurn(PlayerContext context) {
        PlayerId myself = context.getId();
        return new Shoot(myself);
    }

}

William Barbosa

Posted 2014-07-20T19:48:18.813

Reputation: 3 269

3+1 Makes more walls for the Wallers... – Moop – 2014-07-28T16:01:47.103

13

Zombie Rights Activist

The Zombie Rights movement quickly became popular at the offset of the apocalypse. The idea of killing every zombie in sight without remorse is absolutely cruel to them, so they shoot at other players who don't believe in the cause. Understanding the struggle, they will hug zombies if there are no enemies in sight.

package player;
import zombie.*;
import static zombie.Constants.*;
import static java.lang.Math.*;

public class ZombieRightsActivist implements Player {

@Override
public Action doTurn(PlayerContext context) {
    if (context.getBullets() > 0) {
        for (PlayerId player: context.shootablePlayers()) {
            switch(player.getName()) {
                case "ZombieRightsActivist":
                case "DeadBody":
                case "Zombie":   
                    break;
                default:
                    return new Shoot(player);//Kill the non-believers
            }
        }
    }
    double farthest=0;
    Move move=Move.randomMove();
    for (int x = 0; x < VISION_WIDTH; x++) {//Find a lonely zombie and give it a hug
        for (int y = 0; y < VISION_WIDTH; y++) {
            PlayerId friend = context.getPlayField()[x][y];
            if (friend!= null && (friend.getName().equals("Zombie"))) {
                double distance=sqrt(pow(x-context.getX(),2)+pow(y-context.getY(),2));
                if (distance>farthest){
                    farthest = distance;
                    move = Move.inDirection(x - CENTRE_OF_VISION, y -CENTRE_OF_VISION);
                }
            }
        }
    }
    return move;
}

}

Nexus

Posted 2014-07-20T19:48:18.813

Reputation: 641

12

HuddleWolf - Java

Rule 8: Travel in Groups

HuddleWolf takes the sixth rule of Zombieland to heart. It will chase down and huddle with any non-hostile object it sees. If HuddleWolf sees nobody to huddle with, he will venture northeastward in search of more populated areas. HuddleWolf also hates Zombies and will shoot on sight.

HuddleWolf has realized Coward is a much better implementation of his original idea. He bows to Coward's supremacy and now actively prefers the company of Cowards to other nonhostiles.

package player;

import zombie.*;
import static zombie.Constants.*;
import static java.lang.Math.*;

public class HuddleWolf implements Player {

    @Override
    public Action doTurn(PlayerContext context) {
        if (context.getBullets() > 0) {
            for (PlayerId player: context.shootablePlayers()) {
                if (isEnemy(player.getName())) {
                    return new Shoot(player);
                }
            }
        }
        Move bestDirection = Move.NORTHEAST;
        int bestDistance = Integer.MAX_VALUE;
        bool foundACoward = false;
        for (int x = 0; x < VISION_WIDTH; x++) {
            for (int y = 0; y < VISION_WIDTH; y++) {
                int distance = max(abs(x - CENTRE_OF_VISION), abs(y - CENTRE_OF_VISION));
                PlayerId playerAtLocation = context.getPlayField()[x][y];
                if (playerAtLocation != null
                        && !(isEnemy(playerAtLocation.getName()))
                        && !(playerAtLocation.equals(context.getId()))
                        && distance < bestDistance
                        && (!foundACoward || playerAtLocation.getName().equals("Coward"))) {
                    if (playerAtLocation.getName().equals("Coward"))
                    {
                        foundACoward = true;
                    }
                    bestDistance = distance;
                    bestDirection = Move.inDirection(x - CENTRE_OF_VISION, y -CENTRE_OF_VISION);
                }
            }
        }
        return bestDirection;
    }

    private boolean isEnemy(String name) {
        switch(name) {
            case "ZombieRightsActivist":
            case "ZombieHater":
            case "HideyTwitchy" :
            case "Gunner":
            case "Zombie" :
                return true;
            default:
                return false;
        }
    }
}

HuddleWolf

Posted 2014-07-20T19:48:18.813

Reputation: 223

HuddleWolf : Now kills Gunners – HuddleWolf – 2014-07-21T18:46:19.830

You may also want to add && !(playerAtLocation.equals(context.getId())) to your huddling conditions. Right now, it moves towards the nearest player - except the nearest player is you, so it stays still. – James_pic – 2014-07-21T20:21:05.807

Do you mean Rule 8....?

– Pureferret – 2014-07-23T14:01:04.397

@Pureferret : You're right. My original inspiration was not Zombieland cannon. – HuddleWolf – 2014-07-23T15:57:36.613

FYI look at ZombieHater. I don't think he actually hates zombies. He just shoots all of us like the gunner. You probably should add him to list of manditory targets. – kaine – 2014-07-23T18:08:37.740

@anotherguest Yesterday ZombieHater was friendly. >.> – HuddleWolf – 2014-07-23T22:42:08.370

11

Fox

Fox needs a foxhole.

Uses a good part of my Coward but follows a different strategy. If you choose to accept the (sub)mission, fox will choose to build a foxhole.

package player;

import java.lang.Math.*;
import java.util.Set;
import java.util.HashSet;
import zombie.*;
import static zombie.Constants.*;

public class Fox implements Player {

    private static int lastround = -1;
    private static final Set<PlayerId> killed = new HashSet<>();
    private static final Set<PlayerId> looted = new HashSet<>();

    @Override
    public Action doTurn(PlayerContext context) {

        PlayerId[][] field = context.getPlayField();

        // Cleanup
        if (context.getGameClock() > lastround) {
            lastround = context.getGameClock();
            killed.clear();
        }

        // Snipe
        if (context.getBullets() > 0) {
            int distEnemy = 1;
            PlayerId targetEnemy = null;
            for (int x = CENTRE_OF_VISION - SHOOT_RANGE; x <= CENTRE_OF_VISION + SHOOT_RANGE; x++) {
                for (int y = CENTRE_OF_VISION - SHOOT_RANGE; y <= CENTRE_OF_VISION + SHOOT_RANGE; y++) {
                    PlayerId player = field[x][y];
                    if (player != null && !killed.contains(player)) {
                        int dist = getDistance(x, y);
                        if (!player.getName().equals("Zombie") && isEnemy(player.getName()) && dist >= distEnemy ) {
                            distEnemy = dist;
                            targetEnemy = field[x][y];
                        }
                    }
                }
            }
            if (targetEnemy != null) {
                killed.add(targetEnemy);
                return new Shoot( targetEnemy );
            }
        }

        // Check Foxhole
        int foxhole = 0;
        PlayerId target = null;

        for( int x = -2; x <= 2; x++ ) {
            for( int y = -2; y <= 2; y++ ) {
                PlayerId player = field[CENTRE_OF_VISION+x][CENTRE_OF_VISION+y];
                if (player != null && getDistance(CENTRE_OF_VISION+x,CENTRE_OF_VISION+y) == 2) {
                    if (player.getName().equals("DeadBody") || player.getName().equals("Fox")) {
                        foxhole++;
                    }
                    if( player.getName().equals("Zombie")) {
                        target = player;
                    }
                }
            }
        }

        if (context.getBullets() + foxhole >= 16) {
            if (target!=null) {
                return new Shoot( target );
            } else {
                return Move.STAY;
            }
        }

        // Looted?
        for( int xx = CENTRE_OF_VISION-VISION_RANGE+1; xx <= CENTRE_OF_VISION+VISION_RANGE-1; xx++ ) {
            for( int yy = CENTRE_OF_VISION-VISION_RANGE+1; yy <= CENTRE_OF_VISION+VISION_RANGE-1; yy++ ) {
                PlayerId player = field[xx][yy];
                if( player != null && !player.getName().equals("Zombie") && !player.getName().equals("DeadBody")) {
                    for( int x = -1; x <= 1; x++ ) {
                        for( int y = -1; y <= 1; y++ ) {
                            PlayerId loot = field[xx+x][yy+y];
                            if( loot != null && !looted.contains(loot) && loot.getName().equals("DeadBody")) {
                                looted.add(loot);
                            }
                        }
                    }
                }
            }
        }

        // Collect bullets
        int bestScore = -10000000;
        Move bestMove = Move.randomMove();

        for( int x = -1; x <= 1; x++ ) {
            for( int y = -1; y <= 1; y++ ) {
                PlayerId center = field[CENTRE_OF_VISION+x][CENTRE_OF_VISION+y];
                if( center == null ) {
                    int thisScore = 0;
                    for( int xx = CENTRE_OF_VISION+x-VISION_RANGE+1; xx < CENTRE_OF_VISION+x+VISION_RANGE; xx++ ) {
                        for( int yy = CENTRE_OF_VISION+y-VISION_RANGE+1; yy < CENTRE_OF_VISION+y+VISION_RANGE; yy++ ) {
                            PlayerId player = field[xx][yy];
                            if( player != null) {
                                int dist = getDistance(xx-x,yy-y);

                                if( player.getName().equals("DeadBody")) {
                                    if( !looted.contains(player)) {
                                        thisScore += (32+VISION_RANGE-dist)*(VISION_RANGE-dist);
                                    }
                                } else if( player.getName().equals("Zombie")) {
                                    if( dist <= 5 ) {
                                        thisScore -= (int)Math.pow( 10, ( 6 - dist ));
                                    }
                                }
                            }
                        }
                    }
                    if( thisScore > bestScore ) {
                        bestScore = thisScore;
                        bestMove = Move.inDirection( x, y );
                    }
                }
            }
        }

        return bestMove;
    }

    private boolean isEnemy(String name) {
        switch (name) {
            case "Fox":
            case "Coward":
            case "DeadBody":
            case "GordonFreeman":
            case "EmoWolfWithAGun":
            case "HuddleWolf":
            case "ThePriest":
            case "Shotguneer":
            case "Vortigaunt":
            case "Cocoon":
            case "SuperCoward":
            case "SOS":
            case "JohnNash":
            case "MoveRandomly":
                return false;
            default:
                return true;
        }
    }

    private int getDistance(int x, int y) {
        return Math.max(Math.abs(CENTRE_OF_VISION - x), Math.abs(CENTRE_OF_VISION - y));
    }
}

Thaylon

Posted 2014-07-20T19:48:18.813

Reputation: 1 324

I like it. I'm worried I'll set a dangerous precedent by allowing multiple entries (some of the more aggressive entries might be able to gain ground by adding henchmen), but this entry isn't obviously helping Coward (if someone else had written it, they'd probably have done more or less the same thing), so I'll allow it. I should get chance to test it tomorrow. – James_pic – 2014-07-27T17:03:51.503

Added better scoring (no more standing still due to ties) and marks more DeadBodys as looted. Would have liked to try better threat calculation but the testing process takes too long now with coward occassionally running to 15000 rounds. This is probably mainly due to the larger playing field and thus the decreased chances to be spawnkilled by a zombie. Best strategy seems to be: get as much players to round 1000 as possible, stop moving and then let luck decide. – Thaylon – 2014-08-03T01:14:10.547

10

Waller - Java

The Waller loves walls, and seeks them out to hide from zombies. Ideally, the Waller would love to be encased in a wall and wait the apocalypse out.

The Ideal Wall

The ideal wall is where the Waller is completed surround by walls:

   DDD 
   DWD 
   DDD

As the only way to die is to get shot or a zombie spawns underneath or neighboring you. It is the best odds possible for avoiding them.

The ideal wall is hard to come by, but the Waller will find the best possible location and wait for zombies or other players to come to him to shoot and expand his wall.


The algorithm is relatively simple

  1. Is there a zombie about to bite? shoot them.
  2. Find the best wall location (scored 0 - 8) in the field of view
  3. Find the shortest path to that location and run to it!
  4. Try increasing the wall
  5. Wait...

This is a work in progress, feel free to take anything I wrote to use for yourself. I wrote a simple A* Algorithm to find the best path to a desired spot taking into consideration walls and other players. It gets recalculated each round as the walls may have changed between them.


Changelog:

  • Tried to improve early game performance by waiting out and avoiding aggressive players for the first few turns before finding/building walls.

  • Added weights to path finding to take the best route in terms of distance and positional score. Will loot more often now and hopefully not run out of bullets. Improved opening game by running away from aggressive players.

  • Fixed issues with path finding ending on an occupied point

  • Further cleaned code. Added more walls to score a position than just neighboring walls. Expanded how far the Waller will extend his wall.

  • Cleaned code up a bit. Implemented shooting registry to avoid two Wallers shooting the same player during the same turn (inspired by Thaylon)

  • Added path finding between closest zombies and current Walelr. The Waller will only shoot a zombie if it can reach him in a certain number of moves. This hopefully will save some bullets when their is a wall blocking the zombie's path.


Issues

  • The Waller may be in a good position, but sees a better wall location. They will mindless run through zombie-infested land to reach that new location. (I need to discourage this)

  • Early game is rough for the Waller, no good fortification are nearby and lots of aggressive players. (I need to improve early game performace)

  • No communication between Wallers in the same locations. Need to have they work together to built the best wall possible.


Here is the code, I am not a java programmer (C#) so forgive my java errors.

package player;

import java.lang.Math.*;
import java.util.Set;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.ArrayList;
import java.util.List;
import java.util.LinkedList;
import java.util.Collections;
import java.util.Comparator;
import zombie.*;
import static zombie.Constants.*;

public class Waller implements Player {

    private static final int MaximumDistanceToShootZombie = 2;
    private static final int PointsPerWall = 3;
    private static final int PointsPerLoot = 3;
    private static final int PointsPerZombie = -500;
    private static final int PointsPerAggressor = -500;  

    private static final Set<PlayerId> shooting = new HashSet<PlayerId>();
    private static final Set<PlayerId> dontLoot = new HashSet<PlayerId>();
    private static final Set<Point> zombieLocations = new HashSet<Point>();
    private Point CurrentLocation = new Point(CENTRE_OF_VISION, CENTRE_OF_VISION);

    private static int _lastGameTurn = -1;

    // DEBUG
    private static boolean _DEBUG = true;
    private static int agressiveKills;
    private static int zombieKills;
    private static int wallsBuilt;
    ////////

    private static class Point{
        public int X;
        public int Y;
        public PlayerId Player;
        public int Distance;

        public Point(int x, int y) {
            X = x;
            Y = y;
        }

        public Point(int x, int y, PlayerId player) {
            X = x;
            Y = y;
            Player = player;
        }

        public boolean SameLocation(Point otherPoint) {
            return X == otherPoint.X && Y == otherPoint.Y;
        }

        public List<Point> getAdjacentPoints(PlayerId[][] field, int distance, boolean includeSelf) {
            List<Point> points = new ArrayList<Point>();
            for(int x = X - distance; x <= X + distance; x++) {
                for(int y = Y - distance; y <= Y + distance; y++) { 
                    if(!includeSelf && x == X && y == Y)
                        continue;
                    Point pointToAdd = new Point(x, y);                 
                    if(pointToAdd.isValid()) {
                        pointToAdd.Player = field[x][y];
                        points.add(pointToAdd);
                    }
                }
            }                   
            return points;
        }

        public int GetDistance(Point point) {
            return Math.max(Math.abs(X - point.X), Math.abs(Y - point.Y));
        }

        private boolean isValid() { 
            return X >= 0 && X < VISION_WIDTH && Y >= 0 && Y < VISION_WIDTH;
        }

        @Override
        public int hashCode() {
            return (X*100) + Y;  
        }

        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof Point))
                return false;
            if (obj == this)
                return true;

            return SameLocation((Point) obj);       
        }

        @Override
        public String toString(){
            return "("+X+","+Y+")";
        }           
    }

    @Override
    public Action doTurn(PlayerContext context) {   
        int gameTurn = context.getGameClock();  

        if(gameTurn != _lastGameTurn){
            _lastGameTurn = gameTurn;               
        }

        PlayerId[][] field = context.getPlayField();         
        int bullets = context.getBullets();

        // Mark all adjacent dead players as already been looted
        for(Point point : getSurrounding(field, CENTRE_OF_VISION, CENTRE_OF_VISION, 1)){
            if(point.Player.getName().equals("DeadBody")) 
                dontLoot.add(point.Player);  
        }

        int x = context.getX();
        int y = context.getY();
        int boardSize = context.getBoardSize();
        List<Point> newZombies = new ArrayList<Point>();
        for(Point point : getSurrounding(field, CENTRE_OF_VISION, CENTRE_OF_VISION, VISION_RANGE)){     
            Point absolutePoint = GetNewTorusPoint(x + point.X - CENTRE_OF_VISION , y + point.Y - CENTRE_OF_VISION, boardSize);         
            if(point.Player.getName().equals("DeadBody") && zombieLocations.contains(absolutePoint)) 
                dontLoot.add(point.Player);  // new zombie kill
            if(isZombie(point.Player))
                newZombies.add(absolutePoint);
        }
        zombieLocations.clear();
        zombieLocations.addAll(newZombies);

        Action action;  

        // 1) Handle immediate threats to life, have to be dealt before anything else
        action = AssessThreats(field, bullets);
        if(action != null) return action;

        //2) Early turn avoidance
        if(gameTurn < 5) {
            action = EarlyTurn(field, bullets, context);
            if(action != null) return action;
        }

        int currentWallCount = countNumberOfSurroundingWalls(field, CENTRE_OF_VISION, CENTRE_OF_VISION);

        switch(currentWallCount) {  
            case 8:     
                action = ShootAgressivePlayers(field, bullets);
                if(action != null) return action; 
                return Move.STAY; // no more moving                 
            case 7:     
                action = ExpandWall(field, bullets, 1);
                if(action != null) return action;
                action = ShootAgressivePlayers(field, bullets);
                if(action != null) return action;                   
            case 6: 
            case 5:              
            case 4: 
                // action = ExpandWall(field, bullets, 2);
                // if(action != null) return action; 
                // break;
            case 2: 
            case 1: 
            default:                                    
                break;
        }                       

        // 2) Score each possible square and find the best possible location(s)
        Set<Point> optimalLocations = scoreSquares(field);  

        action = findShortestPath(field, CurrentLocation, optimalLocations);
        if(action != null) return action;

        action = ShootAgressivePlayers(field, bullets);
        if(action != null) return action;   

        action = ExpandWall(field, bullets, 1);
        if(action != null) return action;    

        // Stay still if nothing better to do
        return Move.STAY;
    }

    private Action EarlyTurn(PlayerId[][] field, int bullets, PlayerContext context) {
        Point bestPoint = CurrentLocation;
        double bestScore = 1000000;

        for(Point futurePoint : CurrentLocation.getAdjacentPoints(field, 1, true)) {            
            double score = 0;
            for(Point adjacentPoint : futurePoint.getAdjacentPoints(field, VISION_RANGE, false)) {
                if(isAgressive(adjacentPoint.Player)){
                    int dist = futurePoint.GetDistance(adjacentPoint);          
                    if(dist > 6){
                        score += 1;             
                    } else {
                        score += 10000;
                    }
                } else if(isZombie(adjacentPoint.Player)) {
                    int dist = futurePoint.GetDistance(adjacentPoint);      
                    if (dist <= 3)
                        score += 10000;
                } else if(isWall(adjacentPoint.Player)) {
                    score -= 2;
                }
            }   
            if(score < bestScore) {
                bestScore = score;
                bestPoint = futurePoint;
            }
        }                           

        //if(_DEBUG) System.out.println("["+_lastGameTurn+"] Best Score: "+bestScore +" point: "+context.getX()+","+context.getY());

        if(bestPoint == CurrentLocation) {
            Action action = ShootAgressivePlayers(field, bullets);
            if(action != null) return action;   
            return Move.STAY;
        }

        if(bestScore >= 1000) {
            Action action = ShootAgressivePlayers(field, bullets);
            if(action != null) return action;   
        }

        return Move.inDirection(bestPoint.X - CurrentLocation.X, bestPoint.Y - CurrentLocation.Y);      
    }

    private Action ShootAgressivePlayers(PlayerId[][] field, int bullets) {
        if(bullets > 0) {       
            for(Point point : getSurrounding(field, CENTRE_OF_VISION, CENTRE_OF_VISION, SHOOT_RANGE)) {
                PlayerId player = point.Player;
                if(isAgressive(player) && shouldShoot(player)) {
                    if(_DEBUG) System.out.println("["+_lastGameTurn+"] Killing Aggressive: "+(++agressiveKills));       
                    return new Shoot(player);
                }           
            }   
        }
        return null;
    }

    private Action ExpandWall(PlayerId[][] field, int bullets, int distance) {
        if(bullets > 0) {
            for(Point point : getSurrounding(field, CENTRE_OF_VISION, CENTRE_OF_VISION, distance)) {
                PlayerId player = point.Player;
                if(!isWall(player) && isEnemy(player) && !isZombie(player) && shouldShoot(player)) {
                    if(_DEBUG) System.out.println("["+_lastGameTurn+"] Expanding Wall: "+(++wallsBuilt)+" Dist: "+CurrentLocation.GetDistance(point));          
                    return new Shoot(player);
                }           
            }
        }
        return null;
    }

    private boolean shouldShoot(PlayerId player) {
        boolean result = shooting.add(player);
        if(result && isZombie(player)){
            dontLoot.add(player);           
        }       
        return result;      
    }

    private boolean canShoot(PlayerId player) {
        return !shooting.contains(player);      
    }

    private Action AssessThreats(PlayerId[][] field, int bullets){ 
        // Find the most threatening zombie     
        List<Point> bestZombies = new ArrayList<Point>();
        int smallestDistance = MaximumDistanceToShootZombie+1;      
        for(Point point : getSurrounding(field, CENTRE_OF_VISION, CENTRE_OF_VISION, MaximumDistanceToShootZombie)) {
            PlayerId zombie = point.Player;
            if(isZombie(zombie)) {              
                LinkedList<Point> path = findShortestPath_astar(field, CurrentLocation, point, false, false);               
                if(path.isEmpty()) 
                    continue;  
                if(path.size() <= smallestDistance && canShoot(zombie)) {
                    if(path.size() < smallestDistance) {
                        smallestDistance = path.size();
                        bestZombies.clear();
                    }
                    bestZombies.add(point);                                                                                            
                }    
            }
        }

        // No zombies to worry about
        if(bestZombies.isEmpty())
            return null;

        if(bestZombies.size() > 1) {
            if(_DEBUG) System.out.println("["+_lastGameTurn+"] Multiple Zombies in striking range, wait them out?");        
            return MoveToBestSpot(field);   
        }

        Point zombie = bestZombies.get(0);

        // Do we have ammo?
        if(bullets > 0 && shouldShoot(zombie.Player)) { 
            if(_DEBUG) System.out.println("["+_lastGameTurn+"] Shooting Zombie: "+(++zombieKills));             
            return new Shoot(zombie.Player);
        } 

        if(_DEBUG) System.out.println("["+_lastGameTurn+"] No Bullets to Shoot Zombie! Should flee");           
        return MoveInDirection(field, CENTRE_OF_VISION - zombie.X, CENTRE_OF_VISION - zombie.Y);    
    }

    private Action MoveToBestSpot(PlayerId[][] field) { 
        int leastZombies = 100000;
        Point bestPoint = CurrentLocation;
        for(Point point : CurrentLocation.getAdjacentPoints(field, 1, false)) {
            if(point.Player == null) {
                int zombies = countNumberOfSurroundingZombies(field, point.X, point.Y);
                if(zombies < leastZombies) {
                    leastZombies = zombies;
                    bestPoint = point;
                }
            }
        }
        return Move.inDirection(bestPoint.X - CurrentLocation.X, bestPoint.Y - CurrentLocation.Y);
    }

    private Action MoveInDirection(PlayerId[][] field, int x, int y) {
        x = (int)Math.signum(x);
        y = (int)Math.signum(y);

        if(y == 0){
            if(field[CENTRE_OF_VISION+x][CENTRE_OF_VISION] != null)
                return Move.inDirection(x,0);
            if(field[CENTRE_OF_VISION+x][CENTRE_OF_VISION-1] != null)
                return Move.inDirection(x,-1);
            if(field[CENTRE_OF_VISION+x][CENTRE_OF_VISION+1] != null)
                return Move.inDirection(x,1);   
        } else if(x == 0){
            if(field[CENTRE_OF_VISION][CENTRE_OF_VISION+y] != null)
                return Move.inDirection(0,y);
            if(field[CENTRE_OF_VISION-1][CENTRE_OF_VISION+y] != null)
                return Move.inDirection(-1,y);
            if(field[CENTRE_OF_VISION+1][CENTRE_OF_VISION+y] != null)
                return Move.inDirection(1,y);   
        } else {        
            if(field[CENTRE_OF_VISION+x][CENTRE_OF_VISION+y] != null)
                return Move.inDirection(x,y);
            if(field[CENTRE_OF_VISION+x][CENTRE_OF_VISION] != null)
                return Move.inDirection(x,0);
            if(field[CENTRE_OF_VISION][CENTRE_OF_VISION+y] != null)
                return Move.inDirection(0,y);   
        }

        return Move.inDirection(0,0);   
    }

    // Implementation of the A* path finding algorithm
    private LinkedList<Point> findShortestPath_astar(PlayerId[][] field, Point startingPoint, Point finalPoint, boolean includeWeights, boolean considerPlayersAsWalls) {   
        LinkedList<Point> foundPath = new LinkedList<Point>();
        Set<Point> openSet = new HashSet<Point>();
        Set<Point> closedSet = new HashSet<Point>();
        Hashtable<Point, Integer> gScores = new Hashtable<Point, Integer>();
        Hashtable<Point, Point> cameFrom = new Hashtable<Point, Point>();

        gScores.put(startingPoint, 0);
        openSet.add(startingPoint);
        Point currentPoint = startingPoint;

        while(!openSet.isEmpty()) {

            // Find minimum F score
            int minF = 10000000;
            for(Point point : openSet) {
                int g = gScores.get(point);
                int h = point.GetDistance(finalPoint); // Assumes nothing in the way                
                int f = g + h;
                if(f < minF) {
                    minF = f;               
                    currentPoint = point;
                }           
            }

            // Found the final point
            if(currentPoint.SameLocation(finalPoint)) {                 
                Point curr = finalPoint;
                while(!curr.SameLocation(startingPoint)) {
                    foundPath.addFirst(curr);
                    curr = cameFrom.get(curr);
                }
                return foundPath;
            }

            openSet.remove(currentPoint);
            closedSet.add(currentPoint);            

            // Add neighbouring squares
            for(Point pointToAdd : currentPoint.getAdjacentPoints(field, 1, false)){                            
                if(closedSet.contains(pointToAdd) || isWall(pointToAdd.Player) || (considerPlayersAsWalls && pointToAdd.Player != null && !pointToAdd.SameLocation(finalPoint) )) 
                    continue;

                int gScore = gScores.get(currentPoint) + 1; // distance should always be one (may change depending on environment)  
                // if(includeWeights){
                    // gScore += (int)-getScore(field,pointToAdd.X,pointToAdd.Y);
                // }   

                boolean distIsBetter = false;   

                if(!openSet.contains(pointToAdd)) {
                    openSet.add(pointToAdd);
                    distIsBetter = true;
                } else if(gScore < gScores.get(pointToAdd)){                    
                    distIsBetter = true;
                }
                if(distIsBetter) {
                    gScores.put(pointToAdd, gScore);
                    cameFrom.put(pointToAdd, currentPoint);                     
                }
            }  
        }

        return foundPath;   
    }

    private Action findShortestPath(PlayerId[][] field, Point startingPoint, Set<Point> finalPoints) {    
        if(finalPoints.isEmpty())
            return null;
        int smallestPath = 10000;       
        Point pointToMoveTo = startingPoint;  

        for(Point finalPoint : finalPoints) {  
            if(finalPoint == startingPoint)
                return null;
            LinkedList<Point> path = findShortestPath_astar(field, startingPoint, finalPoint, true, true);

            // No path between the two points
            if(path.isEmpty()){
                continue;
            }

            // Check if this is the smallest path
            if(path.size() < smallestPath) {                
                smallestPath = path.size();             
                pointToMoveTo = path.getFirst();                
            }           
        }       

        if(pointToMoveTo == startingPoint)
            return null;

        double score = getScore(field, pointToMoveTo.X, pointToMoveTo.Y);
        if(score < -200) {
            if(_DEBUG) System.out.println("["+_lastGameTurn+"] Best Path leads to a bad spot: "+score);     
            return null;
        }

        return Move.inDirection(pointToMoveTo.X - startingPoint.X, pointToMoveTo.Y - startingPoint.Y);          
    }

    private Set<Point> scoreSquares(PlayerId[][] field) {
        double bestScore = getScore(field, CENTRE_OF_VISION, CENTRE_OF_VISION) + 1; // plus one to break ties, and would rather stay
        Set<Point> bestLocations = new HashSet<Point>();
        if(bestScore >= 0) {
            bestLocations.add(CurrentLocation);         
        } else {
            bestScore = 0;
        }

        for(int x = 0; x < VISION_WIDTH; x++){
            for(int y = 0; y < VISION_WIDTH; y++){   
                if(x == CENTRE_OF_VISION && y == CENTRE_OF_VISION) continue;
                if(field[x][y] == null) {                                 
                    double score = getScore(field, x, y);           
                    if(score >= bestScore){
                        if(score > bestScore) {
                            bestLocations.clear();
                            bestScore = score;   
                        }
                        bestLocations.add(new Point(x, y));                      
                    }
                }
            }
        }       
        return bestLocations;
    }

    private double getScore(PlayerId[][] field, int x, int y) {
        int walls = countNumberOfSurroundingWalls(field, x, y); 
        double score = Math.pow(PointsPerWall, walls);      
        int aggressors = countNumberOfSurroundingAggressions(field, x, y);
        score += aggressors * PointsPerAggressor;   
        int zombies = countNumberOfSurroundingZombies(field, x, y);
        score += zombies * PointsPerZombie;
        int loots = countNumberOfSurroundingLoots(field, x, y);
        score += Math.pow(PointsPerLoot, loots);        
        return score;       
    }

    private int countNumberOfSurroundingZombies(PlayerId[][] field, int x, int y) {     
        int zombies = 0;
        Point currentPoint = new Point(x,y);
        for(Point point : getSurrounding(field, x, y, MaximumDistanceToShootZombie+1)){         
            if(isZombie(point.Player)){
                LinkedList<Point> path = findShortestPath_astar(field, currentPoint, point, false, false);
                if(path.isEmpty()) 
                    continue; 
                if(path.size() < MaximumDistanceToShootZombie+1)
                    zombies++;                  
            }            
        }
        return zombies;           
    }

    private int countNumberOfSurroundingLoots(PlayerId[][] field, int x, int y) {     
        int loots = 0;  
        for(Point point : getSurrounding(field, x, y, 1)){
            PlayerId player = point.Player;
            if(isWall(player) && !dontLoot.contains(player)){   
                loots++;                    
            }            
        }
        return loots;   
    }

    private int countNumberOfSurroundingAggressions(PlayerId[][] field, int x, int y) {     
        int aggressors = 0; 
        for(Point point : getSurrounding(field, x, y, SHOOT_RANGE+1)){
            if(isAgressive(point.Player)){
                aggressors++;                   
            }            
        }
        return aggressors;           
    }

    private int countNumberOfSurroundingWalls(PlayerId[][] field, int x, int y) {
        int walls = 0;      
        for(Point point : getSurrounding(field, x, y, 1)){
            if(isWall(point.Player)){
                walls++;                    
            }            
        }
        return walls;
    }

    private static boolean isZombie(PlayerId player) {
        return player != null && player.getName().equals("Zombie");
    }

    private static boolean isWall(PlayerId player) {
        return player != null && player.getName().equals("DeadBody");       
    }

    private static boolean isEnemy(PlayerId player) {
        if(player == null)
            return false;
        switch (player.getName()) {  
            case "Waller":
            case "DeadBody": 
            case "EmoWolfWithAGun":
                return false;
            default:
                return true;
        }
    }

    private static boolean isAgressive(PlayerId player) {
        if(player == null)
            return false;
        switch (player.getName()) {  
            case "Waller":
            case "DeadBody":   
            case "EmoWolfWithAGun":
            case "GordonFreeman":
            case "Vortigaunt": 
            case "StandStill":
            case "MoveRandomly":
            case "Zombie":
                return false;
            default:
                return true;
        }
    }

    // Helper Functions 

    private List<Point> getSurrounding(PlayerId[][] field, int x, int y, int maxDistance) {      
        final Point currentPoint = new Point(x,y);

        List<Point> players = new ArrayList<Point>();
        int minX = coercePoint(x - maxDistance);
        int maxX = coercePoint(x + maxDistance);
        int minY = coercePoint(y - maxDistance);
        int maxY = coercePoint(y + maxDistance);
        for(int i = minX; i <= maxX; i++){
            for(int j = minY; j <= maxY; j++) {
                if(i == x && j == y) continue;
                if(field[i][j] != null) {                
                    Point point = new Point(i,j,field[i][j]);
                    point.Distance = currentPoint.GetDistance(point);
                    players.add(point);
                }
            }
        }           

        Collections.sort(players, new Comparator<Point>() {
            public int compare(Point p1, Point p2) {
                return Integer.compare(p1.Distance, p2.Distance);          
            }});        

        return players;
    }

    private static int coercePoint(int value) {
        if(value < 0)
            return 0;
        if(value >= VISION_WIDTH)
            return VISION_WIDTH-1;
        return value;
    }

    public static Point GetNewTorusPoint(int x, int y, int boardSize) {
        if(x >= boardSize)
            x = boardSize - x;
        if(y >= boardSize)
            y = boardSize - y;
        return new Point(x,y);
    }

    private static int getDistance(int x1, int y1, int x2, int y2) {
        return Math.max(Math.abs(x1 - x2), Math.abs(y1 - y2));
    }
}

Moop

Posted 2014-07-20T19:48:18.813

Reputation: 723

Ooh, this is nice. I was hoping someone would do something with a real path finding algorithm - I'd been playing round with Dijkstra a little, but this looks like it could make my work obsolete. – James_pic – 2014-07-28T05:43:36.000

@James_pic Thanks, I been playing with him a lot tonight. Lots of interesting things to do when you have path finding. My initial tests put him in third place behind Coward and Shotguneer. – Moop – 2014-07-28T05:46:15.977

Not that one can do anything about it, but fyi, zombies can not only spawn on empty fields or players but under DeadBody's aswell. – Thaylon – 2014-07-28T06:06:46.730

@Thaylon I noticed that while looking at the ouput maps. Nothing I can prepare for anyways, but thanks for looking out for me. – Moop – 2014-07-28T06:07:56.087

It might be a mistake to treat StandStill as a wall. Sure, it doesn't move, but it's no defence against a zombie – James_pic – 2014-07-30T22:33:43.400

FYI, I'm running today's test against the version of Waller from the pull request you sent me yesterday - if that's not the latest version, let me know, and I'll use the right version tomorrow. – James_pic – 2014-07-31T18:55:32.280

I will update a new version tonight you can use tomorrow, I will post the code here. – Moop – 2014-07-31T19:14:39.610

Since you're using A* for routing, one way you could solve the "rushing into zombies" problem is to exclude all squares within biting distance of a zombie in your routing algorithm. I had the same problem with Cocoon. – James_pic – 2014-07-31T22:20:48.307

I tried to do that with weighting function to the best path. Squares near zombies or argos are weighted less. – Moop – 2014-07-31T22:55:29.690

8

Gordon Freeman

Gordon Freeman hates zombies, so he'll never kill himself, but he has no qualms with scavenging for more ammo to shoot more zombies.

package player;

import zombie.*;
import static zombie.Constants.*;
import static java.lang.Math.*;

public class GordonFreeman implements Player {
    @Override
    public Action doTurn(PlayerContext context){
        int ammo = context.getBullets();
        // if I have bullets, shoot some zombies
        if(ammo > 0){
            for(PlayerId player: context.shootablePlayers()){
                switch(player.getName()){
                    case "Zombie":
                       return new Shoot(player);
                    default:
                       break;
                }
            }
        }
        // if no bullets, find a dead body and scavenge
        Move bestDirection = Move.STAY;
        int bestDistance = Integer.MAX_VALUE;
        for(int y = 1; y < VISION_WIDTH - 1; y++) {
            for(int x = 1; x < VISION_WIDTH - 1; x++) {

                PlayerId playerAtLocation = context.getPlayField()[x][y];
                // find a dead body
                if((playerAtLocation != null) && "DeadBody".equals(playerAtLocation.getName())){
                    // check adjacent squares for an empty square
                    for(int yy=-1; yy <= +1; yy++){
                        for(int xx=-1; xx <= +1; xx++){
                            PlayerId playerNearby = context.getPlayField()[x + xx][y + yy];
                            if(playerNearby == null){
                                int distance = max(abs(xx + x - CENTRE_OF_VISION), abs(yy + y - CENTRE_OF_VISION));
                                if(distance < bestDistance){
                                    bestDistance = distance;
                                    bestDirection = Move.inDirection(xx + x - CENTRE_OF_VISION, yy + y - CENTRE_OF_VISION);
                                }
                            }
                        }
                    }
                }
            }
        }
        return bestDirection;
    }
}

Kyle Kanos

Posted 2014-07-20T19:48:18.813

Reputation: 4 270

I hope you don't mind, but I've made a couple of minor edits to your code to get it to compile, and fixed a couple of null pointer or array index bugs, that were making it error at run-time. I haven't changed the logic. – James_pic – 2014-07-21T17:57:23.440

@James_pic: I appreciate the fixes, I'm not a Java programmer in the least, though I like to think that I do well-enough for these KotH challenges :D. – Kyle Kanos – 2014-07-21T18:02:47.533

You might want to take a look at the replay of the last match, at http://jamespic.github.io/zombies/2014-07-21/0.html. It looks like Dr Freeman always walks north-west, which may explain his inconsistent performance.

– James_pic – 2014-07-22T08:21:11.887

@James_pic: Which one is Freeman? There are two G's (one blue, the other yellow). If mine is the blue G, then it seems he moves randomly and not just NW. Also, is there a way to tell between a dead zombie & a dead human? – Kyle Kanos – 2014-07-22T13:59:36.093

If you hover the mouse over a player, it'll tell you who they are, and how many bullets they have. – James_pic – 2014-07-22T14:04:15.137

@James_pic: Okay, so mine is the yellow G. I'll have a look at my code to see if I can make him not go NW. – Kyle Kanos – 2014-07-22T14:06:04.283

@James_pic: I think my recent update should fix the move patterns, it seems I neglected to include where I had seen the dead body (e.g., x and y). – Kyle Kanos – 2014-07-22T14:20:02.333

@KyleKanos hey kyle, I've used a piece of your code, the one for finding a dead body and an empty place near it. Hope you don't mind. – sokie – 2014-07-29T13:20:17.067

@sokie: Feel free to take as needed. – Kyle Kanos – 2014-07-29T13:24:02.563

1Thank you :) I like to reuse code when I can, but I prefer acknowledging when it's not mine. – sokie – 2014-07-29T13:31:59.470

7

The priest

If you have the faith, you don't need to run nor to shot.

package player;

import zombie.*;
import static zombie.Constants.*;
import static java.lang.Math.*;

public class ThePriest implements Player {

    @Override
    public Action doTurn(PlayerContext context) {
        return Move.NORTH;
    }
}

mantale

Posted 2014-07-20T19:48:18.813

Reputation: 191

4HuddleWolf....I see what you did there ;) – Kyle Kanos – 2014-07-22T16:01:27.367

I'm afraid you'll need to name your class something other than HuddleWolf, as that name is already taken – James_pic – 2014-07-22T16:04:50.897

6

ZombieHater

This submission hates zombies! It tries to shoot the nearest zombie while it has bullets, then collects more bullets to kill more zombies.

Edit: ZombieHater now doesn't hesitate to kill other people to get more bullets. It also detects obstacles and tries to go around them.

package player;

import java.awt.Point;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import zombie.*;
import static zombie.Constants.*;

public class ZombieHater implements Player {
    private static final Set<PlayerId> emptyDeadBodies = new HashSet<>();
    private static final Map<PlayerId, Point> lastPos = new HashMap<>();

    @Override
    public Action doTurn(PlayerContext context) {
        PlayerId[][] field = context.getPlayField();
        Point myPos = new Point(context.getX(), context.getY());
        PlayerId myId = context.getId();

        // update dead bodies with the new empty ones
        addEmptyBodies(field);

        // shoot nearest zombie if possible
        if (context.getBullets() > 0) {
            PlayerId nearestZombie = getNearestEnemy(field);
            if (nearestZombie != null) {
                lastPos.remove(myId);
                return new Shoot(nearestZombie);
            }
        }

        // stuck, mostly because of dead body
        if (lastPos.containsKey(myId) && lastPos.get(myId).equals(myPos)) {
            return Move.randomMove();
        }

        // walk towards dead bodies
        Point nearestDeadBody = getNearestDeadBody(field);
        if (nearestDeadBody != null) {
            Move move = Move.inDirection(nearestDeadBody.x - CENTRE_OF_VISION, nearestDeadBody.y - CENTRE_OF_VISION);
            lastPos.put(myId, myPos);
            return move;
        }

        lastPos.remove(myId);
        return Move.randomMove();
    }

    // add surrounding dead bodies to empty bodies
    private void addEmptyBodies(PlayerId[][] field) {
        for (Move move : Move.values()) {
            PlayerId player = field[CENTRE_OF_VISION + move.x][CENTRE_OF_VISION + move.y];
            if (player != null && "DeadBody".equals(player.getName())) {
                emptyDeadBodies.add(player);
            }
        }
    }

    // distance from centre, for example 5 if x=7 and y=3
    private int distanceFromCentre(int x, int y) {
        int dx = Math.abs(CENTRE_OF_VISION - x);
        int dy = Math.abs(CENTRE_OF_VISION - y);
        return Math.max(dx, dy);
    }

    // return nearest enemy or null if none exists
    private PlayerId getNearestEnemy(PlayerId[][] field) {
        int minOffset = Integer.MAX_VALUE;
        PlayerId nearestEnemy = null;
        for (int x = CENTRE_OF_VISION - SHOOT_RANGE; x <= CENTRE_OF_VISION + SHOOT_RANGE; x++) {
            for (int y = CENTRE_OF_VISION - SHOOT_RANGE; y <= CENTRE_OF_VISION + SHOOT_RANGE; y++) {
                int offset = distanceFromCentre(x, y);
                PlayerId player = field[x][y];
                if (player != null && isEnemy(player.getName()) && offset < minOffset) {
                    minOffset = offset;
                    nearestEnemy = field[x][y];
                }
            }
        }
        return nearestEnemy;
    }

   // return nearest dead body or null if none exists
    private Point getNearestDeadBody(PlayerId[][] field) {
        int minOffset = Integer.MAX_VALUE;
        Point nearestDeadBody = null;
        for (int x = CENTRE_OF_VISION - SHOOT_RANGE; x <= CENTRE_OF_VISION + SHOOT_RANGE; x++) {
            for (int y = CENTRE_OF_VISION - SHOOT_RANGE; y <= CENTRE_OF_VISION + SHOOT_RANGE; y++) {
                int offset = distanceFromCentre(x, y);
                PlayerId player = field[x][y];
                if (player != null && "DeadBody".equals(player.getName()) && offset < minOffset && 
                        !emptyDeadBodies.contains(player)) {
                    minOffset = offset;
                    nearestDeadBody = new Point(x, y);
                }
            }
        }
        return nearestDeadBody;
    }

    private boolean isEnemy(String name) {
        switch (name) {
            case "ZombieHater":
            case "DeadBody":
            case "EmoWolfWithGun": // don't bother shooting him
                return false;
            default:
                return true;
        }
    }
}

CommonGuy

Posted 2014-07-20T19:48:18.813

Reputation: 4 684

-1. While different in implementation, this is pretty much the same as my Gordon Freeman....

– Kyle Kanos – 2014-07-22T14:00:59.927

2@KyleKanos. It's the same idea, but implementation details count. Keeping track of which dead bodies have loot is an innovation, for example. – James_pic – 2014-07-22T14:05:55.263

Oh, and on the subject of loot tracking, PlayerId has an appropriate implementation of equals and hashCode, and remains the same throughout a player's "life" (it only changes when they die or turn), so you may find it simpler to keep PlayerIds in emptyDeadBodies than absolute positions. – James_pic – 2014-07-22T14:09:08.967

@KyleKanos it was never my intention to copy your logic. All I did was to create my own bot the way I think it will do the best. – CommonGuy – 2014-07-22T15:35:35.733

@James_pic Oh! That idea did not came to my mind... – CommonGuy – 2014-07-22T15:36:47.593

I updated my submission. It is now more different from @KyleKanos submission. – CommonGuy – 2014-07-22T22:19:50.450

1Nice mods! I was beginning to worry that most strategies were basically the same, but that movement randomisation has given you a huge boost! – James_pic – 2014-07-23T20:02:34.397

6

The Vortigaunt

Will always follow Doctor Gordon Freeman, or walk around aimlessly if he's not on the same dimension.

package player;

import java.util.ArrayList;

import zombie.*;

public class Vortigaunt implements Player {
    class PlayerLocation {
        private int x;
        int y;
        PlayerId player;

        public PlayerLocation(int x, int y, PlayerId id) {
            this.x = x;
            this.y = y;
            this.player = id;
        }

        public int getX() {
            return x;
        }

        public int getY() {
            return y;
        }

        public PlayerId getPlayer() {
            return player;
        }
    }
    @Override
    public Action doTurn(PlayerContext context) {
        PlayerId[][] field = context.getPlayField();
        PlayerLocation me = new PlayerLocation(context.getX(), context.getY(), context.getId());
        ArrayList<PlayerLocation> freemans = findFreeman(field);
        PlayerLocation nearestFreeman = getNearestFreeman(freemans, me);
        if (nearestFreeman == null) {
            return Move.randomMove();
        } else {
            return Move.inDirection(nearestFreeman.getX(), nearestFreeman.getY());
        }
    }

    private PlayerLocation getNearestFreeman(ArrayList<PlayerLocation> freemans, PlayerLocation me) {
        double nearestDistance = Integer.MAX_VALUE;
        PlayerLocation nearestFreeman = null;
        for (PlayerLocation freeman : freemans) {
            int x = freeman.getX() - me.getX();
            int y = freeman.getY() - me.getY();
            double distance = (int)Math.sqrt((double)(x * x + y * y));
            if (distance < nearestDistance) {
                nearestDistance = distance;
                nearestFreeman = freeman;
            }
        }
        return nearestFreeman;
    }

    private ArrayList<PlayerLocation> findFreeman(PlayerId[][] field) {
        ArrayList<PlayerLocation> freemans = new ArrayList<PlayerLocation>();
        for (int x = field.length; x >= 0; x -= 1) {
            for (int y = field[x].length; y >= 0; y -= 1) {
                if (field[x][y].getName().equals("GordonFreeman")) {
                    freemans.add(new PlayerLocation(x, y, field[x][y]));
                }
            }
        }
        return freemans;
    }

}

Kroltan

Posted 2014-07-20T19:48:18.813

Reputation: 517

So you never shoot... but stick close to someone who does... I can't tell if this will get Freeman killed but this will be awesome for the zombie populations... – kaine – 2014-07-25T19:17:24.890

6

Cocoon - Frege

Such a shame. Half a dozen languages to choose from, and everyone just uses Java. Well, no sense in letting them go to waste, so here's a competitor in Frege.

It uses Dijkstra's algorithm to find a secluded spot to wait out the apocalypse, goes foraging for bullets if it runs out, and shoots zombies if they get too close.

Updated

Cocoon now ignores routes that would take it within striking distance of zombies in its routing algorithm, and shoots zombies when they're within 2 squares, rather than 3 (to build a tighter cocoon).

module player.Cocoon where
  import zombie.FregeBindings
  import frege.data.TreeMap
  import Data.List(sortBy)
  import Data.Foldable(minimumBy, maximumBy)

  instance Ord PlayerId where
    a <=> b = case a.getName <=> b.getName of
      Eq -> a.getNumber <=> b.getNumber
      x -> x

  instance Show Action where
    show action = action.toString

  -- Dijkstras shortest path algorithm
  data DijkstraNode = Green {d :: Int, pos :: (Int, Int)} | Red {pos :: (Int, Int)} | Yellow {d :: Int, pos :: (Int, Int)}
  data DijkstraState = DijkstraState {board :: Tree (Int, Int) DijkstraNode, yellows :: TreeSet DijkstraNode}
  derive Eq DijkstraNode
  derive Ord DijkstraNode
  derive Show DijkstraNode
  derive Show DijkstraState

  updateState :: Int -> DijkstraState -> (Int, Int) -> DijkstraState
  updateState d (oldState@DijkstraState {board, yellows}) pos  = case (lookup board pos) of
    Nothing -> oldState
    Just Green {d, pos} -> oldState
    Just Red {pos} -> let
        newYellow = Yellow d pos
        newYellows = insert yellows newYellow ()
        newBoard = update board pos newYellow
      in DijkstraState {board = newBoard, yellows = newYellows}
    Just (oldYellow@Yellow {d = oldD, pos = oldPos})
          | oldD <= d = oldState
          | true = let
              newYellow = Yellow d pos
              newYellows = insert (delete yellows oldYellow) newYellow ()
              newBoard = insert board pos newYellow
            in DijkstraState {board = newBoard, yellows = newYellows}

  neighbours :: (Int, Int) -> [(Int, Int)]
  neighbours (x,y) = [(x1 + x, y1 + y) | x1 <- [-1 .. 1], y1 <- [-1 .. 1], x1 != 0 || y1 != 0]

  moveRegion = [(x, y) | x <- [-1 .. 1], y <- [-1 .. 1]]

  findMove :: DijkstraState -> Maybe Move
  findMove DijkstraState {board, yellows}
     | null yellows = Nothing
     | true = let
         tip@Yellow{d, pos} = head (keys yellows)
         rest = delete yellows tip
         newBoard = insert board pos (Green d pos)
         intermediateState = DijkstraState {board = newBoard, yellows = rest}
         neighbourhood = [node | pos <- moveRegion , node <- lookup board pos]
       in if tip.pos == (0, 0)
          then case minimum neighbourhood of
            _ | null neighbourhood = Nothing
            Green {d, pos = (x,y)} -> Just (Move.inDirection x y)
            _ -> Nothing
          else findMove (fold (updateState (d + 1)) intermediateState (neighbours pos))

  insertRed :: Tree (Int, Int) DijkstraNode -> (Int, Int) -> Tree (Int, Int) DijkstraNode
  insertRed board pos = insert board pos (Red {pos})

  removeZombieTerritory :: PlayerContext -> Tree (Int, Int) DijkstraNode -> Tree (Int, Int) DijkstraNode
  removeZombieTerritory ctx board =
    let
      zombies = [pos | pos@(x,y) <- v2, pid <- ctx.lookAround x y, pid.getName == "Zombie"]
      zombieTerritory = [(x + xx, y + yy) | (x,y) <- zombies, xx <- [-2..2], yy <- [-2..2]]
    in fold Tree.delete board zombieTerritory

  v = [-visionRange .. visionRange]
  v2 = sortBy (comparing dist) [(x,y) | x <- v, y <- v]

  shootable = sortBy (comparing dist) [(x, y) | x <- [-shootRange .. shootRange], y <- [-shootRange .. shootRange]]

  moveTo :: (Int, Int) -> PlayerContext -> Maybe Move
  moveTo pos ctx =
    let
      rawBoard = fold insertRed Tree.empty ([p | p@(x, y) <- v2,
                                                  ctx.lookAround x y == Nothing] ++ [(0,0)])
      board = removeZombieTerritory ctx rawBoard
      yellows = Tree.insert Tree.empty (Yellow {d = 0, pos}) ()
    in findMove (DijkstraState {board, yellows})

  dist :: (Int, Int) -> Int
  dist (x,y) = max (abs x) (abs y)

  findBullets :: PlayerContext -> TreeSet PlayerId -> Maybe Action
  findBullets ctx emptyBodies =
    if (ctx.getBullets > 0) then Nothing
    else
      let
        viableBodies = [pos | pos@(x,y) <- v2, pid <- (ctx.lookAround x y), pid.getName == "DeadBody", lookup emptyBodies pid == Nothing]
      in case viableBodies of
         target : _ -> moveTo target ctx
         _ -> Nothing

  isThreat :: String -> (Int, Int) -> Bool
  isThreat name pos = case (name, pos) of
    ("Zombie", pos) | dist pos <= 2 -> true
    ("HideyTwitchy", _) -> true
    ("ZombieHater", _) -> true
    ("ZombieRightsActivist", _) -> true
    ("Gunner", _) -> true
    _ -> false

  shootThreats :: PlayerContext -> Maybe Action
  shootThreats ctx =
    let
      threats = [pid | pos@(x, y) <- shootable, pid <- ctx.lookAround x y, isThreat (pid.getName) pos]
    in case threats of
      target:_ | ctx.getBullets == 0 = Nothing
               | true = Just (Shoot.new target)
      _ -> Nothing

  coziness :: PlayerContext -> (Int, Int) -> Int
  coziness ctx (x,y) =
    let
      wallScores = [3 - dist (xx, yy) | xx <- [-2 .. 2],
                                        yy <- [-2 .. 2],
                                        xx != 0 || yy != 0,
                                        pid <- ctx.lookAround (x + xx) (y + yy),
                                        pid.getName == "DeadBody"]
    in 3 * sum wallScores - dist (x,y)

  gotoCoziest :: PlayerContext -> Maybe Action
  gotoCoziest ctx =
    let
      emptySquares = [pos | pos@(x, y) <- v2, ctx.lookAround x y == Nothing] ++ [(0,0)]
      coziest = maximumBy (comparing (coziness ctx)) emptySquares
    in if null emptySquares then Nothing
       else moveTo coziest ctx

  updateEmptyBodies :: PlayerContext -> TreeSet PlayerId -> TreeSet PlayerId
  updateEmptyBodies ctx current =
    let
      nearbyBodies = [pid | (x,y) <- neighbours (0,0), pid <- ctx.lookAround x y, pid.getName == "DeadBody"]
    in fold (\x -> \y -> insert x y ()) current nearbyBodies

  doStep :: TreeSet PlayerId -> PlayerContext -> Continue
  doStep !bodies ctx =
    let
      emptyBodies = updateEmptyBodies ctx bodies
      plan = (findBullets ctx emptyBodies) `mplus` (shootThreats ctx) `mplus` (gotoCoziest ctx)
    in case plan of
      Just action -> Continue {result = action, andThen = doStep emptyBodies}
      Nothing -> Continue {result = Move.stay, andThen = doStep emptyBodies}

  doTurn = doStep Tree.empty

James_pic

Posted 2014-07-20T19:48:18.813

Reputation: 3 988

4

Gunner - Java

Here's an example to get you off the blocks. He shoots anything he sees, or wanders aimlessly if there's nothing around, or he's got no bullets.

package player;

import zombie.*;

public class Gunner implements Player {

    @Override
    public Action doTurn(PlayerContext context) {
        if (context.getBullets() > 0) {
            for (PlayerId player: context.shootablePlayers()) {
                switch(player.getName()) {
                    case "Gunner":
                    case "DeadBody":
                        break;
                    default:
                        return new Shoot(player);
                }
            }
        }
        return Move.randomMove();
    }

}

Can you do any better?

James_pic

Posted 2014-07-20T19:48:18.813

Reputation: 3 988

3

Shotguneer

I will admit that my main goal is to shoot the Gunner.

package player;

import zombie.*;
import static zombie.Constants.*;
import static java.lang.Math.*;

public class Shotguneer implements Player {

    @Override
    public Action doTurn(PlayerContext context) {

        double sdistance=1000;

        if (context.getBullets() > 0) {
            for (PlayerId player: context.shootablePlayers()) {
                switch(player.getName()) {
                    case "Gunner":
                    case "ZombieRightsActivist":
                    case "HideyTwitchy":
                    case "ZombieHater":
                    case "Waller";
                    case "Bee";
                    case "SunTzu";
                    //case "Fox":
                    //case "Coward":
                        return new Shoot(player);
                    default:
                        break;
                }
            }
            boolean zombies=false;
            PlayerId TargetZombie = context.getId();
            for (int x = -3; x < +4; x++) {
            for (int y = -3; y < +4; y++) {
                double distance = sqrt(pow(x,2)+pow(y,2));
                PlayerId playerAtLocation = context.getPlayField()[x + CENTRE_OF_VISION][y + CENTRE_OF_VISION];
                if (playerAtLocation != null && playerAtLocation.getName().equals("Zombie") && (distance < sdistance ||zombies==false)) {
                    sdistance = distance;
                    zombies=true;
                    TargetZombie=playerAtLocation;
                }
                //if (playerAtLocation != null && playerAtLocation.getName().equals("Priest") && distance < 2 &&zombies==false) {
                    //TargetZombie=playerAtLocation;
                    //sdistance=distance;
                //}
            }}
            if (zombies || sdistance<3) {
                return new Shoot(TargetZombie);
            }
        }

        if (context.getPlayField()[CENTRE_OF_VISION-1][CENTRE_OF_VISION-1]==null){
            return Move.NORTHWEST;  
        } else if (context.getPlayField()[CENTRE_OF_VISION][CENTRE_OF_VISION-1]==null){
            return Move.NORTH;
        } else {
            return Move.WEST;
        }

    }

}

kaine

Posted 2014-07-20T19:48:18.813

Reputation: 536

@Benny how do you do that so you don't have to edit anymore? – kaine – 2014-07-22T21:05:04.927

As it stands, this doesn't compile. I had a go at fixing it up, but there are a few places where I can't figure out what the original intention of the code was (for example, the compiler complains that TargetZombie may not be initialized, and I'm not sure what you'd like to do). Do you want to take another swing at it? – James_pic – 2014-07-22T22:14:37.073

Thank you for trying. I hadn't setup your code so it failed because it didn't recognize your constants. I didn't remember it would not compile if the TargetZombie did not have a default value (though it would never be used if it wasn't thanks to the zombies constant). I've modified it so it kills me if I am wrong on that!. Don't waste you time trying to edit it unless you are bored, I will write another comment if I can confirm it works alongside the other players. – kaine – 2014-07-23T13:07:57.160

I fixed it up so it would compile and run without errors - mostly "off by CENTRE_OF_VISION" errors, and some NPEs. You'll have to take a look at the run (http://jamespic.github.io/zombies/2014-07-23/0.html) to see if it's doing what you expect.

– James_pic – 2014-07-23T20:00:00.900

It looks like you've had a big impact on the life expectancy of Gunners! I wonder if this will spark a wave of top competitors taking each other out? – James_pic – 2014-07-23T20:05:23.697

@James_pic Well thank you, I will study what you changed intently. I did have to change the code to kill wo other kinds of jerks who have adopted Gunner main strategy as Zombie fanboys and so called Zombie Haters who hate everyone else just as much....whew... Again Thank you! – kaine – 2014-07-23T20:35:35.027

To be honest, I don't really understand this bot, but the latest version of Shotguneer seems to wipe the floor with the competition. AFAICT, part of the reason seems to be that it ends up in "forts". I always figured that would be a good strategy - it seems to work in zombie movies. – James_pic – 2014-07-24T20:58:35.653

That was the initial goal of how it shoots some enemies but I didn't want it to be obvious. I'm working on another specifically designed to do that better. – kaine – 2014-07-25T12:33:46.163

It's OK if it's obvious how your code works. I'd say it's even preferable. There's nothing really at stake here, other than some reputation points, and if people understand how you did it, they can learn from you. – James_pic – 2014-07-25T15:40:59.293

@James_pic please update my kill list when you get a chance. It is pretty clear the cowardfox guy kills me whenever he sees me like gunner so I need to add him to my murderers list. – kaine – 2014-07-28T12:44:08.997

@kaine I had it on friendly, i removed it because of: if (playerAtLocation != null && playerAtLocation.getName().equals("Shotguneer") != true && distance < 2 &&zombies==false) which basically means you shoot anything that gets close to you? (Btw, it should still be on the friendly list in the repository, changed it only after your change) – Thaylon – 2014-07-28T13:36:53.640

@thaylon I know why. Yours was designed to shoot indiscriminately at times too. Just if you shoot me any chance you get, I need to do the same. Just making sure it gets on the next update. Won't change your score much as you'll have two shooting me every time compared to 1 shooting you. – kaine – 2014-07-28T13:42:48.857

@kaine Alright. Didnt mean to complain nor worry about the score. Just wanted to add that its not without reason, and im perfectly willing to add any friendly bot to the no-enemy list. – Thaylon – 2014-07-28T13:56:00.720

@kaine I'll update the control program with the latest versions of everyone's code this evening, and re-run. I haven't had chance to run this over the weekend, as I was visiting family. – James_pic – 2014-07-28T13:59:14.813

No worries at all, I really like this challenge if you hadn't guessed... – kaine – 2014-07-28T14:00:44.717

@Thaylon I've removed any ability to shoot others who don't explicitly shoot him. Remove your shooting him from either of yours and I will remove mine. Rather ticks me off but, hey, its part of the game. – kaine – 2014-07-28T20:31:28.110

@kaine Added Shotguneer as friendly to both – Thaylon – 2014-07-28T20:35:17.627

3

HideyTwitchy

Nutjob that hides from everything unless a player chases him down to shooting range, in which case he panics and shoots (including his own kind). Only loots bodies if he's out of ammo, then gets the heck away from the corpses.

package player;

import static java.lang.Math.*;
import java.awt.Point;
import java.util.HashSet;
import java.util.Set;

import zombie.*;
import static zombie.Constants.*;

public class HideyTwitchy implements Player {

    private Set<Integer> lootedCorpseIds = new HashSet<Integer>();

    @Override
    public Action doTurn(PlayerContext context) {
        Action action = null;

        Point playerP = getClosestPlayerPoint(context);
        Point corpseP = getClosestCorpsePoint(context); 
        Point enemyP = getClosestEnemyPoint(context);

        if (isWithinArea(playerP, Constants.SHOOT_RANGE, Constants.SHOOT_RANGE)) {
            //player spotted within 5x5
            if (context.getBullets() > 0) {
                action = getShootAction(playerP, context); //shoot!
            } else {
                action = getMoveAwayFromPoint(playerP); //run!
            }
        } else if (isWithinArea(enemyP, Constants.VISION_RANGE, Constants.VISION_RANGE)) {
            //players or zombie spotted within 8x8
            action = getMoveAwayFromPoint(enemyP); //run!
        } else if (isWithinArea(corpseP, Constants.VISION_RANGE, Constants.VISION_RANGE)) {
            //corpse spotted within 8x8

            int uniqueCorpseId = getPlayerIdAtPoint(context, corpseP).getNumber();
            if (isWithinArea(corpseP, 1, 1)) {
                //loot the corpse and get the heck away from it
                lootedCorpseIds.add(uniqueCorpseId);
                action = getMoveAwayFromPoint(corpseP);
            } else if (context.getBullets() == 0 && !lootedCorpseIds.contains(uniqueCorpseId)) {
                action = getMoveTowardsPoint(corpseP); //loot corpse if not looted!
            } 
        } else {
            //randomly move
            action = Move.randomMove();
        }

        return action;
    }

    private PlayerId getPlayerIdAtPoint(PlayerContext context, Point p) {
        return context.getPlayField()[(int) p.getX()][(int) p.getY()];
    }

    private Move getMoveTowardsPoint(Point p) {
        return Move.inDirection((int)p.getX() - CENTRE_OF_VISION, (int)p.getY() - CENTRE_OF_VISION);
    }

    private Move getMoveAwayFromPoint(Point p) {
        return Move.inDirection(CENTRE_OF_VISION - (int)p.getX(), CENTRE_OF_VISION - (int)p.getY());
    }

    private Shoot getShootAction(Point p, PlayerContext context) {
        PlayerId id = context.getPlayField()[(int) p.getX()][(int) p.getY()];
        Shoot shootAction = new Shoot(id);

        return shootAction;
    }

    private boolean isWithinArea(Point p, int x, int y) {
        return p != null 
                && abs(CENTRE_OF_VISION - p.getX()) <= x
                && abs(CENTRE_OF_VISION - p.getY()) <= y;
    }

    private Point getClosestEnemyPoint(PlayerContext context) {
        String[] lookFor = {};
        String[] avoid = {Dead.DEADBODYNAME};
        Point p = getClosestEntity(context, lookFor, avoid);

        return p;
    }

    private Point getClosestPlayerPoint(PlayerContext context) {
        String[] lookFor = {};
        String[] avoid = {Dead.DEADBODYNAME, Dead.ZOMBIENAME};
        Point p = getClosestEntity(context, lookFor, avoid);

        return p;
    }

    private Point getClosestCorpsePoint(PlayerContext context) {
        String[] lookFor = {Dead.DEADBODYNAME};
        String[] avoid = {Dead.ZOMBIENAME};
        Point p = getClosestEntity(context, lookFor, avoid);

        return p;
    }

    private Point getClosestEntity(PlayerContext context, String[] lookFor, String[] avoid) {

        int bestDistance = Integer.MAX_VALUE;
        Point closestPoint = null;

        for (int x = 0; x < VISION_WIDTH; x++) {
            for (int y = 0; y < VISION_WIDTH; y++) {


                PlayerId playerAtLocation = context.getPlayField()[x][y];

                if (playerAtLocation != null && !playerAtLocation.equals(context.getId())) {
                    //not empty and not me

                    boolean conditionsMet = true;
                    for (String lookForName : lookFor) {
                        conditionsMet |= playerAtLocation.getName().equals(lookForName);
                    }

                    for (String avoidName : avoid) {
                        conditionsMet &= !playerAtLocation.getName().equals(avoidName);
                    }

                    if (conditionsMet) {
                        int distance = max(abs(x - CENTRE_OF_VISION), abs(y - CENTRE_OF_VISION));
                        if (distance < bestDistance) {
                            bestDistance = distance;
                            closestPoint = new Point(x, y);
                        }
                    }
                }
            }
        }

        return closestPoint;
    }
}

Hopper Hunter

Posted 2014-07-20T19:48:18.813

Reputation: 71

3

SuperCoward - JAVA I know about the double submission but I just couldn't resist. Please tell me if I should remove it.

What kind of coward is that whom shoots and fights? Presenting to you SUPER Coward, he will run on the field trying to avoid who he thinks are enemies and zombies. He tries to stay safe and avoids obstacles. If he doesn't find a good route, panics and stays in place

package player;

import zombie.*;
import static zombie.Constants.*;

import static java.lang.Math.abs;
import static java.lang.Math.max;

import java.awt.Point;

public class SuperCoward implements Player {

    private enum DANGER{
        SAFE(0),PROBABLY_SAFE(1),UNSAFE(2),DANGER(3);

        private int value;
        private DANGER(int value){
            this.value = value;
        }
    }

    private final int PLAYER_X = 8;
    private final int PLAYER_Y = 8;

    @Override
    public Action doTurn(PlayerContext context) {

        DANGER danger = DANGER.DANGER;
        Point position = null;
        for(int i=-1;i<1;i++){
            for(int j=-1;j<1;j++){
                DANGER positionDanger = isDangerous(context,PLAYER_X+i,PLAYER_Y+j);
                if(positionDanger.value < danger.value){
                    if(canMove(context,PLAYER_X+i,PLAYER_Y+j)){
                        position = new Point(PLAYER_X+i, PLAYER_Y+j);
                    }
                }
            }
        }

        if(position != null){
            return Move.inDirection(position.x, position.y);
        }else{
            return Move.STAY;
        }
    }

    private boolean canMove(PlayerContext context,int posX, int posY){
         PlayerId playerAtLocation = context.getPlayField()[posX][posY];
        if(playerAtLocation == null){
            return true;
        }else{
            return false;
        }
    }

    private DANGER isDangerous(PlayerContext context,int posX, int posY){
        DANGER danger = DANGER.SAFE;

          for (int x = 0; x < VISION_WIDTH; x++) {
                for (int y = 0; y < VISION_WIDTH; y++) {
                     PlayerId playerAtLocation = context.getPlayField()[x][y];

                     if(playerAtLocation != null && isEnemy(playerAtLocation.getName())){
                         int distanceToPlayer = max(abs(x - posX), abs(y - posY));
                         if(playerAtLocation.getName().equals("Zombie")){
                             DANGER currentDanger = null;
                             if(distanceToPlayer <=3){
                                 currentDanger = DANGER.DANGER;
                             }else if(distanceToPlayer <=5){
                                 currentDanger = DANGER.PROBABLY_SAFE;
                             }else if(distanceToPlayer >5){
                                 currentDanger = DANGER.SAFE;
                             }
                             if(currentDanger.value > danger.value){
                                 danger = currentDanger;
                             }
                         }else{
                             DANGER currentDanger = null;
                             if(distanceToPlayer <=5){
                                 currentDanger = DANGER.DANGER;
                             }else if(distanceToPlayer >5){
                                 currentDanger = DANGER.PROBABLY_SAFE;
                             }
                             if(currentDanger.value > danger.value){
                                 danger = currentDanger;
                             }
                         }
                     }
                }
          }
        return danger;
    }

    private boolean isEnemy(String name){
         switch(name) {
            case "DeadBody":
            case "GordonFreeman":
            case "EmoWolfWithAGun":
            case "HuddleWolf":
            case "ThePriest":
            case "Shotguneer":
            case "SuperCoward":
                return false;
            default:
                return true;
         }
    }
}

sokie

Posted 2014-07-20T19:48:18.813

Reputation: 151

3+1 for DANGER danger = DANGER.DANGER; – Andreï Kostyrka – 2016-09-09T13:03:07.240

My current thinking is that I'll allow double submissions as long as there's no collusion between them - so entries that could just as easily have been submitted by different players are OK, but henchmen or lapdogs are not. This looks like it should be OK (unless someone objects), so I'll test it when I get chance. – James_pic – 2014-07-29T14:01:33.657

2

Sokie - JAVA

Sokie knows that you are better in packs, so it tries to go the nearest ally he finds. While moving, if he is in danger tries to fight or otherwise tries to run. When he reaches friends, they all fight together until they have no ammo, then try to find nearest bodies to scavenge.

package player;

import zombie.*;
import static zombie.Constants.*;

import static java.lang.Math.abs;
import static java.lang.Math.max;

import java.awt.Point;
import java.util.HashMap;
import java.util.Map;

public class Sokie implements Player {

    public static Map<Point, Sokie> myPack = new HashMap<>();
    private PlayerContext context;
    private Move moveDirection;
    private final int PLAYER_X = 8;
    private final int PLAYER_Y = 8;

    private enum DANGER {
        SAFE(0), PROBABLY_SAFE(1), UNSAFE(2), DANGER(3);

        private int value;

        private DANGER(int value) {
            this.value = value;
        }
    }

    @Override
    public Action doTurn(PlayerContext context) {
        Point p = new Point(context.getX(), context.getY());
        myPack.put(p, this);
        this.context = context;

        int friends = 0;
        int deadbodyDistance = Integer.MAX_VALUE;
        Move deadbodyDirection = null;
        Point deadBodyPosition = null;
        Move friendsDirection = Move.SOUTHWEST;

        // Find the closest friend to whom we can move
        int maxDistance = Integer.MAX_VALUE;
        for (Sokie bp : myPack.values()) {
            // Skip ourselves
            if (bp.context.equals(context)) {
                continue;
            }
            Point pos = bp.getPosition();
            int x = pos.x;
            int y = pos.y;
            int distance = Math.max(Math.abs(context.getX() - x),
                    Math.abs(context.getY() - y));
            if (distance < maxDistance) {
                if (canMove(context, (int) Math.signum(x), (int) Math.signum(y))
                        && !isDangerous(context, (int) Math.signum(x),
                                (int) Math.signum(y))) {
                    maxDistance = distance;
                    friendsDirection = Move.inDirection((int) Math.signum(x),
                            (int) Math.signum(y));
                } else {
                    if (canMove(context, (int) Math.signum(0),
                            (int) Math.signum(y))
                            && !isDangerous(context, (int) Math.signum(x),
                                    (int) Math.signum(y))) {
                        maxDistance = distance;
                        friendsDirection = Move.inDirection(
                                (int) Math.signum(0), (int) Math.signum(y));
                    } else if (canMove(context, (int) Math.signum(x),
                            (int) Math.signum(0))
                            && !isDangerous(context, (int) Math.signum(x),
                                    (int) Math.signum(y))) {
                        maxDistance = distance;
                        friendsDirection = Move.inDirection(
                                (int) Math.signum(x), (int) Math.signum(0));
                    }
                }
            }
        }

        // Find how many friends we have in close vicinity
        for (int x = 0; x < VISION_WIDTH; x++) {
            for (int y = 0; y < VISION_WIDTH; y++) {
                PlayerId playerAtLocation = context.getPlayField()[x][y];
                if (playerAtLocation != null
                        && playerAtLocation.getName().equals("Sokie")) {
                    friends++;
                }
            }
        }

        // Search for dead bodies
        for (int y = 1; y < VISION_WIDTH - 1; y++) {
            for (int x = 1; x < VISION_WIDTH - 1; x++) {

                PlayerId playerAtLocation = context.getPlayField()[x][y];
                // find a dead body
                if ((playerAtLocation != null)
                        && "DeadBody".equals(playerAtLocation.getName())) {
                    // check adjacent squares for an empty square
                    for (int yy = -1; yy <= +1; yy++) {
                        for (int xx = -1; xx <= +1; xx++) {
                            PlayerId playerNearby = context.getPlayField()[x
                                    + xx][y + yy];
                            if (playerNearby == null) {
                                int distance = max(abs(xx + x
                                        - CENTRE_OF_VISION), abs(yy + y
                                        - CENTRE_OF_VISION));
                                if (distance < deadbodyDistance) {
                                    deadbodyDistance = distance;
                                    deadBodyPosition = getAbsolutePosition(
                                            context, x + xx, y + yy);
                                    deadbodyDirection = Move.inDirection(xx + x
                                            - CENTRE_OF_VISION, yy + y
                                            - CENTRE_OF_VISION);
                                }
                            }
                        }
                    }
                }
            }
        }

        // If we have atleast 2 people close, stay or try to shoot
        // otherwise move randomly, try to find bodies and packs
        if (friends >= 2) {
            // Shoot anybody close
            if (context.getBullets() > 0) {
                int distEnemy = VISION_WIDTH;
                int distZombie = VISION_WIDTH;
                PlayerId targetEnemy = null;
                PlayerId targetZombie = null;
                for (int x = CENTRE_OF_VISION - SHOOT_RANGE; x <= CENTRE_OF_VISION
                        + SHOOT_RANGE; x++) {
                    for (int y = CENTRE_OF_VISION - SHOOT_RANGE; y <= CENTRE_OF_VISION
                            + SHOOT_RANGE; y++) {
                        PlayerId player = context.getPlayField()[x][y];
                        if (player != null) {
                            int dist = getDistance(x, y);
                            if (player.getName().equals("Zombie")) {
                                if (dist < distZombie) {
                                    distZombie = dist;
                                    targetZombie = player;
                                }
                            } else if (isEnemy(player.getName())
                                    && dist <= distEnemy) {
                                distEnemy = dist;
                                targetEnemy = context.getPlayField()[x][y];
                            }
                        }
                    }
                }

                if (targetZombie != null && distZombie <= 2) {
                    return new Shoot(targetZombie);
                } else if (targetEnemy != null && distEnemy <= 5) {
                    return new Shoot(targetEnemy);
                }
            }

            for (Sokie bp : myPack.values()) {
                // If someone in the pack has ammo, stay
                if (bp.getAmmo() > 0) {
                    return Move.STAY;
                }
            }

            // If there are bodies close, try to reach them
            int bodyDistance = deadbodyDistance;
            if (deadbodyDistance <= 5) {
                for (Sokie bp : myPack.values()) {
                    int distanceBody = Math.max(
                            Math.abs(deadBodyPosition.x - bp.context.getX()),
                            Math.abs(deadBodyPosition.y - bp.context.getY()));
                    if (deadbodyDistance > distanceBody) {
                        bodyDistance = distanceBody;
                    }
                }
            }
            // If we are not the closest to the body, stay
            if (bodyDistance < deadbodyDistance) {
                return Move.STAY;
            } else {
                return deadbodyDirection;
            }
        } else {
            // We try to reach our closest friend
            // If we are in danger, either fight or run
            if (areWeInDanger(context, PLAYER_X, PLAYER_Y)) {
                if (context.getBullets() > 0) {
                    int distEnemy = VISION_WIDTH;
                    int distZombie = VISION_WIDTH;
                    PlayerId targetEnemy = null;
                    PlayerId targetZombie = null;
                    for (int x = CENTRE_OF_VISION - SHOOT_RANGE; x <= CENTRE_OF_VISION
                            + SHOOT_RANGE; x++) {
                        for (int y = CENTRE_OF_VISION - SHOOT_RANGE; y <= CENTRE_OF_VISION
                                + SHOOT_RANGE; y++) {
                            PlayerId player = context.getPlayField()[x][y];
                            if (player != null) {
                                int dist = getDistance(x, y);
                                if (player.getName().equals("Zombie")) {
                                    if (dist < distZombie) {
                                        distZombie = dist;
                                        targetZombie = player;
                                    }
                                } else if (isEnemy(player.getName())
                                        && dist <= distEnemy) {
                                    distEnemy = dist;
                                    targetEnemy = context.getPlayField()[x][y];
                                }
                            }
                        }
                    }

                    if (targetZombie != null && distZombie <= 2) {
                        return new Shoot(targetZombie);
                    } else if (targetEnemy != null && distEnemy <= 5) {
                        return new Shoot(targetEnemy);
                    }
                } else {
                    DANGER danger = DANGER.DANGER;
                    Point position = null;
                    for (int i = -1; i < 1; i++) {
                        for (int j = -1; j < 1; j++) {
                            DANGER positionDanger = getDangerLevel(context,
                                    PLAYER_X + i, PLAYER_Y + j);
                            if (positionDanger.value < danger.value) {
                                if (canMove(context, PLAYER_X + i, PLAYER_Y + j)) {
                                    position = new Point(PLAYER_X + i, PLAYER_Y
                                            + j);
                                }
                            }
                        }
                    }

                    if (position != null) {
                        return Move.inDirection(position.x, position.y);
                    } else {
                        return Move.randomMove();
                    }
                }
            } else {
                return friendsDirection;
            }
        }
        return Move.randomMove();
    }

    private DANGER getDangerLevel(PlayerContext context, int posX, int posY) {
        DANGER danger = DANGER.SAFE;

        for (int x = 0; x < VISION_WIDTH; x++) {
            for (int y = 0; y < VISION_WIDTH; y++) {
                PlayerId playerAtLocation = context.getPlayField()[x][y];

                if (playerAtLocation != null
                        && isEnemy(playerAtLocation.getName())) {
                    int distanceToPlayer = max(abs(x - posX), abs(y - posY));
                    if (playerAtLocation.getName().equals("Zombie")) {
                        DANGER currentDanger = null;
                        if (distanceToPlayer <= 2) {
                            currentDanger = DANGER.DANGER;
                        } else if (distanceToPlayer <= 5) {
                            currentDanger = DANGER.PROBABLY_SAFE;
                        } else if (distanceToPlayer > 5) {
                            currentDanger = DANGER.SAFE;
                        }
                        if (currentDanger.value > danger.value) {
                            danger = currentDanger;
                        }
                    } else {
                        DANGER currentDanger = null;
                        if (distanceToPlayer <= 5) {
                            currentDanger = DANGER.DANGER;
                        } else if (distanceToPlayer > 5) {
                            currentDanger = DANGER.PROBABLY_SAFE;
                        }
                        if (currentDanger.value > danger.value) {
                            danger = currentDanger;
                        }
                    }
                }
            }
        }
        return danger;
    }

    private boolean isDangerous(PlayerContext context, int posX, int posY) {

        for (int x = 0; x < VISION_WIDTH; x++) {
            for (int y = 0; y < VISION_WIDTH; y++) {
                PlayerId playerAtLocation = context.getPlayField()[x][y];

                if (playerAtLocation != null
                        && isEnemy(playerAtLocation.getName())) {
                    int distanceToPlayer = max(abs(x - posX), abs(y - posY));
                    if (playerAtLocation.getName().equals("Zombie")) {
                        if (distanceToPlayer <= 2) {
                            return true;
                        }
                    } else {
                        if (distanceToPlayer <= 5) {
                            return true;
                        }
                    }
                }
            }
        }
        return false;

    }

    // calculates absolute position, from XY in our field of view
    private Point getAbsolutePosition(PlayerContext context, int relativeX,
            int relativeY) {
        int playerX = context.getX();
        int playerY = context.getY();

        return new Point(playerX + (relativeX - PLAYER_X), playerY
                + (relativeY - PLAYER_Y));
    }

    // Gets distance on the field
    private int getDistance(int x, int y) {
        return Math.max(Math.abs(PLAYER_X - x), Math.abs(PLAYER_Y - y));
    }

    public int getAmmo() {
        return context.getBullets();
    }

    public Point getPosition() {
        Point p = new Point(context.getX(), context.getY());
        return p;
    }

    public Move getMoveDirection() {
        return moveDirection;
    }

    // Quick check for dangers around us
    private boolean areWeInDanger(PlayerContext context, int posX, int posY) {
        for (int x = 0; x < VISION_WIDTH; x++) {
            for (int y = 0; y < VISION_WIDTH; y++) {
                PlayerId playerAtLocation = context.getPlayField()[x][y];

                if (playerAtLocation != null
                        && isEnemy(playerAtLocation.getName())) {
                    int distanceToPlayer = max(abs(x - posX), abs(y - posY));
                    if (playerAtLocation.getName().equals("Zombie")) {
                        if (distanceToPlayer <= 2) {
                            return true;
                        }
                    } else {
                        if (distanceToPlayer <= 5) {
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }

    private boolean canMove(PlayerContext context, int posX, int posY) {
        PlayerId playerAtLocation = context.getPlayField()[posX][posY];
        if (playerAtLocation == null) {
            return true;
        } else {
            return false;
        }
    }

    private boolean isEnemy(String name) {
        switch (name) {
        case "Sokie":
        case "DeadBody":
        case "GordonFreeman":
        case "EmoWolfWithAGun":
        case "HuddleWolf":
        case "ThePriest":
        case "Shotguneer":
        case "StandStill":
            return false;
        default:
            return true;
        }
    }

}

sokie

Posted 2014-07-20T19:48:18.813

Reputation: 151

FYI, the name of your player will be the same as the name of your class (in this case, "Sokie"). So I suspect playerAtLocation.getName().equals("BetterInPacks2") should probably be playerAtLocation.getName().equals("Sokie"). – James_pic – 2014-07-29T14:04:29.977

Dang! That was some leftover code while I was testing different implementations, I'll correct it in no time! Thank you – sokie – 2014-07-29T14:19:34.173

2

Bee - Python

Second entry, but I thought it would be fun to try something else out and in a different language.

  • Now the Bees will avoid moving to the same spot.
  • More 'Pythonic' now
  • Optimized torus movement so the bees can get to the queen quicker (thanks James for allowing access to the board size)

Bees prefer to be together, so they designate one of the surviving Bees as the Queen Bee and swarm toward her. They will sting opponents on their way to her, preferring zombie flesh over human.

from zombie import Player, Move, Shoot, PlayerRegistry, Constants

friends = ['Bee','Waller','DeadBody','ThePriest','StandStill','Vortigaunt','EmoWolfWithAGun']
MID = Constants.CENTRE_OF_VISION
BOARDSIZE = 1
sign = lambda x: (1, -1)[x<0]
isZombie = lambda player: player and player.getName() is "Zombie"
isEnemy = lambda player: player and player.getName() not in friends
isWall = lambda player: player and (player.getName() is "DeadBody" or player.getName() is "StandStill")
distance = lambda x1,y1,x2,y2: max(distance1d(x1,x2), distance1d(y1,y2))
distance1d = lambda x1,x2: min(abs(x1-x2), BOARDSIZE - abs(x1-x2))
Bees = {}
Shot = set()
MoveTo = set()  

def getDirection(x1, x2):  
    diff = x1 - x2  
    if abs(diff) > (BOARDSIZE // 2):
        return sign(diff)
    return -sign(diff)

class Bee(Player):  
    Queen = None
    QueenBeePosition = None
    X = Y = ID = 0
    LastTurn = -1   

    def doTurn(self, context): 
        global BOARDSIZE
        self.ID = context.id.number
        self.X = context.x
        self.Y = context.y
        BOARDSIZE = context.boardSize  
        self.setQueenBee(context.gameClock)                    
        action = self.sting(context)
        if action:
            return action
        return self.moveToQueenBee(context)     

    def setQueenBee(self, turn):
        if turn != Bee.LastTurn:
            Bee.LastTurn = turn     
            MoveTo.clear() # Clear the move set on new turn
        Bees[self.ID] = turn # Report In        
        if not Bee.Queen or (Bee.Queen and Bees[Bee.Queen] < turn - 1):
            Bee.Queen = self.ID
            Bee.QueenBeePosition = (self.X, self.Y)     

    def moveToQueenBee(self, context):
        if self.ID == Bee.Queen:
            return Move.randomMove()

        dist = distance(Bee.QueenBeePosition[0], Bee.QueenBeePosition[1], self.X, self.Y)
        if dist < 4:
            return Move.randomMove()

        signX = getDirection(self.X, Bee.QueenBeePosition[0])      
        signY = getDirection(self.Y, Bee.QueenBeePosition[1])      
        walls = 0
        field = context.playField
        for (deltaX, deltaY) in [(signX,signY),(signX,0),(0,signY),(signX,-signY),(-signX,signY)]:
            player = field[MID + deltaX][MID + deltaY]
            if isWall(player):
                walls += 1
            if not player:               
                point = frozenset([self.X+deltaX,self.Y+deltaY])            
                if point not in MoveTo:
                    MoveTo.add(point)                   
                    return Move.inDirection(deltaX,deltaY)
        if walls > 2:
            return Move.randomMove()
        return Move.STAY

    def sting(self, context):      

        if context.bullets < 1:
            return      
        field = context.playField
        closestZombie,closestPlayer = None,None
        closestZombieDist,bestDist = 3,5   
        for x in range(MID - 5, MID + 5):
            for y in range(MID - 5, MID + 5):
                player = field[x][y]
                if player and not isWall(player) and player not in Shot:
                    dist = distance(MID,MID,x,y)
                    if isZombie(player) and dist < closestZombieDist:   
                        closestZombieDist = dist
                        closestZombie = player
                    elif isEnemy(player) and dist < bestDist: 
                        bestDist = dist
                        closestPlayer = player

        if closestZombie:
            Shot.add(closestZombie)
            return Shoot(closestZombie)        

        if closestPlayer:
            Shot.add(closestPlayer)
            return Shoot(closestPlayer)        

PlayerRegistry.registerPlayer("Bee", Bee())

Moop

Posted 2014-07-20T19:48:18.813

Reputation: 723

1Phew! I was starting to worry there'd be nothing but Java in this contest. – James_pic – 2014-07-29T21:39:28.070

Yeah, and the first entry that searches the field globally, which I think is a neat feature. – Moop – 2014-07-29T21:40:27.427

One thing you may find useful is that Jython automatically makes getters and setters for JavaBeans available as properties. For example, you can use context.gameClock rather than context.getGameClock(). – James_pic – 2014-07-31T08:33:06.893

@James_pic Thanks for the tip, didn't know that. – Moop – 2014-07-31T14:46:45.913

2

Mr. Assassin

His parents aren't very nice people. He has no qualms killing but won't if he thinks you will help him live longer. He treats everyone as if they are special and then disgards that and ranks you by how close he wants you, how much he wants you dead, and important you are to the current situation.

As I know alot of this is hidden in here I'll explain some. He usually never kill the guys who might kill his threats before he has to as they are not his targets. He will feed off some who never shoot. With one exception, he kills you if you currently kill him. He prefers walled in locations without that being forced into the code. He is Fox phobic and a Coward coward. He cuddles up with Huddlewolves and walls up with Wallers. He currently moves toward groupers (like Sokie even though Sokie shoots him mercilessly). Nash should love him as his priorites make a game theorist cry. His customers, however, seem to be very zenophobic having him kill priests, aliens, and newcomers (though he mmight pardon you friday or saturday if you don't kill him... sunday is too late). Sorry if I am forgetting anyone else.

package player;
import zombie.*;
import static zombie.Constants.*;
//import static java.lang.Math.*;

public class Jack implements Player {
    @Override
    public Action doTurn(PlayerContext context) {
        int[] Ideal = {1,5,8,7,2,2,7,2,1,5,1,2,1,1,7,2,7,7,7,0,2,3,1,7};
        int[] Threat = {1,4,8,8,1,1,7,1,2,2,2,1,2,0,6,2,6,6,6,1,1,2,6,6};
        int[] Importance = {1,2,4,4,1,1,1,1,3,1,3,1,3,3,1,2,1,1,1,10,2,2,3,2};

        PlayerId Target = context.getId();
        int[][] Bob = {{800-2*Math.max(0,context.getGameClock()),400-Math.max(0,context.getGameClock()),800-Math.max(0,context.getGameClock())},{0,0,0},{0,0,0}};
        double maxDanger=0;
        int zombies=0;

        for (int x = -8; x < +8; x++) {
        for (int y = -8; y < +8; y++) {
            PlayerId playerAtLocation = context.getPlayField()[x + CENTRE_OF_VISION][y + CENTRE_OF_VISION];
            if (playerAtLocation != null && x*y+x+Math.abs(y) != 0){
                if (Math.abs(x)*Math.abs(y)==1 || Math.abs(x) + Math.abs(y)==1){
                    Bob[x+1][y+1]-=100000;
                }
                int dist = Math.max(Math.abs(x),Math.abs(y));
                int Ident = Dats(playerAtLocation);
                double Danger = (Threat[Ident]-dist)*Importance[Ident];
                if(Ident==1 && dist<Threat[Ident]){
                    zombies++;
                    if(context.getPlayField()[TFSAE(x)-1 + CENTRE_OF_VISION][TFSAE(y) -1+ CENTRE_OF_VISION]!=null){ 
                    Danger=0;
                                } else if(dist==2){Danger+=4;} 
                }
                if(Danger>maxDanger && dist<6){
                    maxDanger=Danger;
                    Target=playerAtLocation;
                }
                if(dist != Ideal[Ident]){

                    Bob[TFSAE(x)][TFSAE(y)] += Math.round(200*Importance[Ident]/(dist-Ideal[Ident]));

                    if(TFSAE(x) ==1) {
                        Bob[0][TFSAE(y)] += Math.round(100*Importance[Ident]/(dist-Ideal[Ident]));
                        Bob[2][TFSAE(y)] += Math.round(100*Importance[Ident]/(dist-Ideal[Ident]));
                    } else {
                        Bob[1][TFSAE(y)] += Math.round(100*Importance[Ident]/(dist-Ideal[Ident]));
                    }

                    if(TFSAE(y) ==1) {
                        Bob[TFSAE(x)][0] += Math.round(100*Importance[Ident]/(dist-Ideal[Ident]));
                        Bob[TFSAE(x)][2] += Math.round(100*Importance[Ident]/(dist-Ideal[Ident]));
                    } else {
                        Bob[TFSAE(x)][1] += Math.round(100*Importance[Ident]/(dist-Ideal[Ident]));
                    }
                }
            }
        }}

        if (context.getBullets()>1 && maxDanger>0){
            return new Shoot(Target);
        } else if (context.getBullets()==1 && zombies>3){
            return new Shoot(context.getId());
        } else if (context.getBullets()==1 && maxDanger>7){
            return new Shoot(Target);
        }

        int Xmax=0;
        int Ymax=0;

        for (int x = 0; x < 3; x++) {
        for (int y = 0; y < 3; y++) {
            if (Bob[x][y]>=Bob[Xmax][Ymax]){
                Xmax=x;
                Ymax=y;
            }
        }}
        return Move.inDirection(Xmax-1, Ymax-1);

    }

    private int Dats (PlayerId WhoDat){
        switch (WhoDat.getName()){
            case "DeadBody": return 0;
            case "Zombie": return 1;
            case "Fox": return 2;
            case "Coward": return 3;
            case "Shotguneer": return 4;
            case "HuddleWolf": return 5;
            case "Sokie": return 6;
            case "GordonFreeman": return 7;
            case "Vortigaunt": return 8;
            case "SuperCoward": return 9;
            case "StandStill": return 10;
            case "JohnNash": return 11;
            case "MoveRandomly": return 12;
            case "Waller": return 13;
            case "HideyTwitchy": return 14;
            case "Bee": return 15;
            case "ZombieHater": return 16;
            case "ZombieRightsActivist": return 17;
            case "Gunner": return 18;
            case "EmoWolfWithAGun": return 19;
            case "Jack": return 20;
              case "SOS": return 21;
              case "SunTzu": return 22;
            default: return 23;
        }

    }
    private int TFSAE(int TBN){
        if(TBN==0){return 1;
        } else if(TBN>0){return 2;}

        return 0;
    }
}

kaine

Posted 2014-07-20T19:48:18.813

Reputation: 536

Oh yeah, Jack actually does save the last bullet for himself. If there are more than a certain number of zombies around him and he has one bullet left he will kill himself to prevent the spread. Since it doesn't matter if I win (I can't give myself the bounty) I wanted some character to have that functionality. – kaine – 2014-08-01T17:09:16.073

@James_pic This worked in the game (though a previous version I had cloned a few days ago). It should work. If it doesn't let me know. If it doesn't on sunday delete it. – kaine – 2014-08-01T22:28:15.213

It seems to work OK in the current version of the code. I'll include it in a test later today. – James_pic – 2014-08-02T12:09:37.463

Again, I have no idea how this entry works. If anyone else had submitted it, I'd assume it was non-working nonsense, but since Shotguneer is currently beating my best entry, I'm genuinely curious to see what it actually does. – James_pic – 2014-08-02T12:25:02.197

Pretty complex and, i assume, handtuned threat/distance/priority management. – Thaylon – 2014-08-02T21:16:33.843

1

John Nash - Javascript

The decision whether to shoot someone is essentially the prisoner's dilemma. If you assume that your adversary has already made up their mind, then the best option is always to shoot them. However, if your adversary will decide what to do based on what they think you'll do, then the best option is to leave them be, and they'll probably do the same.

John Nash only shoots "chumps" - adversaries who have already made up their minds. That is, he shoots adversaries that always shoot, or adversaries that never shoot. He leaves adversaries alone if they have more complicated logic.

When he's not shooting, he's scavenging for bullets or heading South.

var Constants = Packages.zombie.Constants
var Shoot = Packages.zombie.Shoot
var Move = Packages.zombie.Move
var Player = Packages.zombie.Player
var PlayerRegistry = Packages.zombie.PlayerRegistry

function mkSet() {
    var s = {}
    for (var i = 0; i < arguments.length; i++) {
        s[arguments[i]] = true
    }
    return s
}

var chumps = mkSet(
                "GordonFreeman",
                "HideyTwitchy",
                "Gunner",
                "MoveRandomly",
                "StandStill",
                "ThePriest",
                "Vortigaunt",
                "ZombieHater",
                "ZombieRightsActivist",
                "Bee",
                "Zombie",
                "SuperCoward"
              )

function dist(x, y) {
    return Math.max(Math.abs(x - Constants.CENTRE_OF_VISION), Math.abs(y - Constants.CENTRE_OF_VISION))
}

function range(width, offset) {
    var x = []
    for (var i = -width; i <= width; i++) {
        for (var j = -width; j <= width; j++) {
            if (i != 0 || j != 0) x.push([i + offset,j + offset])
        }
    }
    return x
}

function JohnNash() {
    var looted = {}
    this.doTurn = function(context) {
        var field = context.getPlayField()
        // Save looted bodies
        range(1, Constants.CENTRE_OF_VISION).forEach(function(p) {
            var x = p[0], y = p[1]
            var playerId = field[x][y]
            if (playerId && playerId.getName() == "DeadBody") {
                looted[playerId] = true
            }
        })

        // Shoot any nearby chumps
        if (context.getBullets() > 0) {
            var shootableIterator = context.shootablePlayers().iterator();
            while (shootableIterator.hasNext()) {
                var shootable = shootableIterator.next()
                if (chumps[shootable.getName()]) return new Shoot(shootable)
            }
        }

        // Helper function - everyone loves closures
        function moveTowards(x, y) {
            var tryMove = Move.inDirection(
                    x - Constants.CENTRE_OF_VISION,
                    y - Constants.CENTRE_OF_VISION
            )
            if (!(field[Constants.CENTRE_OF_VISION + tryMove.x][Constants.CENTRE_OF_VISION + tryMove.y])) {
                return tryMove
            } else {
                // If your path is blocked, take a random move
                return Move.randomMove()
            }
        }

        // Loot
        var bestX, bestY, bestDist = Infinity
        range(Constants.VISION_RANGE, Constants.CENTRE_OF_VISION).forEach(function(p) {
            var x = p[0], y = p[1]
            var playerId = field[x][y]
            if (playerId
                    && playerId.getName() == "DeadBody"
                    && !looted[playerId]
                    && dist(x, y) < bestDist) {
                bestDist = dist(x,y)
                bestX = x
                bestY = y
            }
        })

        if (bestDist < Infinity) {
            return moveTowards(bestX, bestY)
        }
        else return Move.SOUTH
    }
}

PlayerRegistry.registerPlayer("JohnNash", new Player(new JohnNash()))

James_pic

Posted 2014-07-20T19:48:18.813

Reputation: 3 988

Does MoveRandomly do anything besides moving randomly? – Thaylon – 2014-08-01T08:28:37.500

@Thaylon Nope, it does exactly what it says on the tin. Moop included it in a pull request he sent to the project. The main purpose of the pull request was to improve the HTML output, but also included MoveRandomly, and I figured I'd include MoveRandomly in the test to see what happens in the test. – James_pic – 2014-08-01T09:07:12.940

I pulled the latest from your github and couldn't get john-nash.js to compile – Moop – 2014-08-01T13:53:53.057

@moop I found the same. – Pureferret – 2014-08-01T16:42:10.010

Fixed it. I'd only tested it against Java 7. It should work in 8 as well now – James_pic – 2014-08-01T21:50:26.010

1

S.O.S. (Shoot on Sight)

package player;

import zombie.*;
import static zombie.Constants.*;
import static java.lang.Math.*;

public class SOS implements Player {

    @Override
    public Action doTurn(PlayerContext context) {
        if (context.getBullets() > 0) {
            for (PlayerId player: context.shootablePlayers()) {
                switch(player.getName()) {
                    case "Gunner":
                    case "Zombie":
                    case "ZombieRightsActivist":
                        return new Shoot(player);
                    default:
                        break;
                }
            }
        }
        Move bestDirection = Move.NORTH;
        int bestDistance = Integer.MAX_VALUE;
        for (int x = 0; x < VISION_WIDTH; x++) {
            for (int y = 0; y < VISION_WIDTH; y++) {
                int distance = max(abs(x - CENTRE_OF_VISION), abs(y - CENTRE_OF_VISION));
                PlayerId playerAtLocation = context.getPlayField()[x][y];
                if (playerAtLocation != null
                        && !(playerAtLocation.getName().equals("Zombie"))
                        && !(playerAtLocation.getName().equals("Gunner"))
                        && !(playerAtLocation.getName().equals("ZombieRightsActivist"))
                        && !(playerAtLocation.getName().equals("ZombieHater"))
                        && !(playerAtLocation.equals(context.getId()))
                        && distance < bestDistance) {
                    bestDistance = distance;
                    bestDirection = Move.inDirection(x - CENTRE_OF_VISION, y -CENTRE_OF_VISION);
                }
            }
        }
        return bestDirection;
    }
}

Timtech

Posted 2014-07-20T19:48:18.813

Reputation: 12 038

1

SunTzu tries to be tactical, and find safe spots on the grid to move to. But as it stands, he's just a work in progress atm.

“Thus we may know that there are five essentials for victory:
1. He will win who knows when to fight and when not to fight.
2. He will win who knows how to handle both superior and inferior forces.
3. He will win whose army is animated by the same spirit throughout all its ranks.
4. He will win who, prepared himself, waits to take the enemy unprepared.
5. He will win who has military capacity and is not interfered with by the sovereign.”

package player;

import static zombie.Constants.CENTRE_OF_VISION;
import static zombie.Constants.SHOOT_RANGE;
import static zombie.Constants.VISION_RANGE;

import java.awt.Point;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;

import zombie.Action;
import zombie.Move;
import zombie.Player;
import zombie.PlayerContext;
import zombie.PlayerId;
import zombie.Shoot;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.Table.Cell;
import com.google.common.collect.TreeBasedTable;

    public class SunTzu implements Player {
        private TreeBasedTable<Integer, Integer, Integer> dangerZone;
        private final static int IN_ENEMY_RANGE = 5;
        private static final int IN_LOOTED_RANGE = 4;
        private static final int FULL_MAGAZINE = 10;
        private static final int IN_ZOMBIE_RANGE = 10;
        private static final int NUM_PLAYERS = 40;
        private LinkedHashSet<Point> safeSpots;
        private PlayerId[][] localAreas;
        private Set<PlayerId> looted= new HashSet<>(50*NUM_PLAYERS);
        private int ammo;
        PlayerId biggestThreat;
        private Set<PlayerId> shootable;
        private PlayerId myId;
        @SuppressWarnings("unused")
        @Override
        public Action doTurn(PlayerContext context) {
            ammo = context.getBullets();
            int gameTurn =context.getGameClock();
            int boardSize = context.getBoardSize();
            myId = context.getId();
            localAreas = context.getPlayField();
            dangerZone = TreeBasedTable.create();
            shootable = context.shootablePlayers();
            updateAdjacentBodyState();

            for (int x = CENTRE_OF_VISION - SHOOT_RANGE; x <= CENTRE_OF_VISION + SHOOT_RANGE; x++) {
                for (int y = CENTRE_OF_VISION - SHOOT_RANGE; y <= CENTRE_OF_VISION + SHOOT_RANGE; y++) {
                    PlayerId playerId = localAreas[x][y];
                    if (playerId != null) {
                        calculateDangerZone(x,y,playerId);
                    }
                }
            }
            Action myAction = null;
            Iterator<Point> pIt = safeSpots.iterator();
            if (ammo>0&&!pIt.hasNext()&&getBiggestThreat()!=null) {
                return new Shoot(getBiggestThreat());
            } else if (pIt.hasNext()){
                Point p=pIt.next();
                return Move.inDirection(p.x, p.y);
            }else{
                return Move.randomMove();
            }
        }

        private PlayerId getBiggestThreat() {
            return biggestThreat==null?shootable.iterator().next():biggestThreat;
        }

        public void setBiggestThreat(PlayerId biggestThreat) {
            this.biggestThreat = biggestThreat;
        }
        private void updateAdjacentBodyState() {

            for( int x = -1; x <= 1; x++ ) {
                for( int y = -1; y <= 1; y++ ) {
                    PlayerId adjPlayerId = localAreas[CENTRE_OF_VISION+x][CENTRE_OF_VISION+y];
                    if( adjPlayerId != null && (!looted.contains(adjPlayerId) && adjPlayerId.getName().equals("DeadBody"))) {
                        looted.add(adjPlayerId);
                    }       
                }
            }
        }

        private void calculateDangerZone(int x, int y, PlayerId playerId) {
            deriveDanger(playerId, x, y);
            safeSpots = getSafeSpots();
        }

        @SuppressWarnings("rawtypes")
        private LinkedHashSet<Point> getSafeSpots() {
            LinkedHashSet<Point> safeSpots = new LinkedHashSet<>();
            TreeSet<Cell> spots = new TreeSet<>(cellValueComparator());
            for (Cell<Integer, Integer, Integer> cell : dangerZone.cellSet()) {
                spots.add(cell);
            }
            final Cell safeCell = spots.isEmpty()?null:Collections.min(spots,cellValueComparator());
            Function<Cell,Point> pointFromCell = new Function<Cell,Point>() {
                public Point apply(final Cell arg0) {return new Point((int)arg0.getRowKey(), (int)arg0.getColumnKey());};
            };

            if (safeCell!=null) {
                safeSpots.addAll(Collections2.transform(
                        Collections2.filter(spots, sameCellValuePredicate(safeCell)), pointFromCell));
            }
            return safeSpots;
        }

        @SuppressWarnings("rawtypes")
        private Predicate<Cell> sameCellValuePredicate(final Cell safeCell) {
            return new Predicate<Cell>() {

                @Override
                public boolean apply(Cell arg0) {
                    return (arg0.getValue() == safeCell.getValue());
                }
            };
        }

        @SuppressWarnings("rawtypes")
        private Comparator<Cell> cellValueComparator() {
            return new Comparator<Cell>() {
                @Override
                public int compare(Cell o1, Cell o2) {
                    return (int)o1.getValue()- (int)o2.getValue();
                }
            };
        }

        private void deriveDanger(PlayerId playerId, int x, int y) {
            switch (playerId.getName()) {
            case "Gunner":
            case "Fox":
            case "HideyTwitchy":
            case "Shotguneer":
            case "ZombieRightsActivist":
            case "ZombieHater":
            case "SuperCoward":
            case "Sokie":
                updateDangerZoneWithEnemy(x, y);
                break;
            case "DeadBody":
            case "Zombie":
                updateDangerZoneWithBodies(x,y);
                break;
            default:
                break;
            }
        }

        private void updateDangerZoneWithBodies(int x, int y) {
            int dangerLevel=0;
            if(localAreas[x][y].getName().equalsIgnoreCase("Zombie")){
                dangerLevel = IN_ZOMBIE_RANGE;
            }
            else if(looted.contains(localAreas[x][y])){
                dangerLevel = IN_LOOTED_RANGE;
            }else{
                dangerLevel = Math.min(-1,-FULL_MAGAZINE+ammo);
            }
            for (int i = x-1; i < x+1; i++) {
                for (int j = y-1; j < y+1; j++) {
                    Integer previousDangerLevel = dangerZone.get(i, j) ;
                    int currentDangerLevel = dangerLevel;
                    if (previousDangerLevel != null) {
                        currentDangerLevel = previousDangerLevel+dangerLevel;
                    } 
                    dangerZone.put(x, y, currentDangerLevel);
                }
            }
        }

        private void updateDangerZoneWithEnemy(int x, int y) {
            int dangerLevel = IN_ENEMY_RANGE;
            playerShieldFound:
                for (int i = Math.max(x-SHOOT_RANGE, 0); i < Math.min(SHOOT_RANGE+x,VISION_RANGE); i++) {
                    for (int j = Math.max(y-SHOOT_RANGE, 0); j < Math.min(SHOOT_RANGE+y,VISION_RANGE); j++) {
                        int cardinalityFactor = (i+1)+(j+1);
                        Integer previousDangerLevel = dangerZone.get(i, j);
                        int currentDangerLevel = dangerLevel*cardinalityFactor;
                        PlayerId enemy = localAreas[x][y];
                        PlayerId target = localAreas[i][j];
                        if (target!=null) {
                            if (target != enemy) {
                                break playerShieldFound;
                            } else if (target.equals(myId)) {
                                setBiggestThreat(enemy);
                            }
                        }
                        if (previousDangerLevel != null) {
                            currentDangerLevel = Math.max(previousDangerLevel, dangerLevel);
                        } 
                        dangerZone.put(i, j, currentDangerLevel );
                    }
                }
        }

    }

Current issues are that the dangerzone is not created properly, and I don't think biggestThreat is being populated correctly.

Pureferret

Posted 2014-07-20T19:48:18.813

Reputation: 960

+1 for using classes from Guava. I'd forgotten that I used it in the control programme, but it does have lots of useful classes for precisely this sort of thing. – James_pic – 2014-08-01T07:28:04.523

1@james_pic tbh, I'd sell out for the Apache equivalent. Feel like I've made a hash of it. – Pureferret – 2014-08-01T08:59:25.840

Since it's built with Maven, I can add any reasonable dependencies you might want, if it makes things easier. If you want to use Commons Collections, go for it. – James_pic – 2014-08-01T09:08:47.817

The thing is, @james_pic I barely got it to build with maven as I don't really understand it... I'd rather work with what other have but get more done. – Pureferret – 2014-08-01T16:35:06.740

1

Tyzoid - my somewhat dumb robot

package player;

import java.util.ArrayList;

import zombie.Action;
import zombie.Move;
import zombie.Player;
import zombie.PlayerContext;
import zombie.PlayerId;
import zombie.Shoot;

public class Tyzoid implements Player {
    private static final int minPathDistance = 7;
    private static final int pathLength = 10;
    private static final boolean debug = false;
    private static final int max_iterations = 5000;

    private int current_iterations = 0;

    private class Situation {
        public int hostiles = 0;
        public int scores[][] = new int[21][21];
        public ArrayList<Coordinate> path = new ArrayList<Coordinate>();
        public int distanceToHostile = 10;
        public Coordinate nearestHostile = new Coordinate(0,0);
        public boolean seriousHostile = false;

        // Minimum path score allowed to move under normal circumstances
        public int pathScore = -40;

        public int bulletsLeft = 0;

        public Situation(){
            path.add(new Coordinate(10,10));
        }
    }

    public class Coordinate {
        public int x = 0;
        public int y = 0;

        public Coordinate(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }

    @Override
    public Action doTurn(PlayerContext context) {
        try {
            Situation currentSituation = this.evaluateSituation(context);
            return this.makeDecision(currentSituation, context);
        } catch (Exception e) {
            if (debug) e.printStackTrace();
            return Move.STAY;
        }
    }

    private Situation evaluateSituation(PlayerContext context) {
        Situation situation = new Situation();

        for (int i = 0; i < 21; i++) {
            for (int j = 0; j < 21; j++) {
                situation.scores[i][j] = -3;
            }
        }

        situation.bulletsLeft = context.getBullets();

        PlayerId[][] visibleBoard = context.getPlayField();

        for (int bx = 0; bx < visibleBoard.length; bx++) {
            for (int by = 0; by < visibleBoard[bx].length; by++) {
                if (visibleBoard[bx][by] == null) {
                    continue;
                }

                if (this.isHostile(visibleBoard[bx][by].getName(), false)) {
                    situation.hostiles++;

                    this.hostileDetected(situation, bx, by, context);
                } else if (visibleBoard[bx][by].getName().equals("DeadPlayer")) {
                    this.friendlyDetected(situation, bx, by);
                    // OVER 9000!!! (there's an obstacle)
                    situation.scores[bx + 2][by + 2] = -9001;
                }
            }
        }

        return situation;
    }

    private Action makeDecision(Situation currentSituation, PlayerContext context) {
        if ((currentSituation.distanceToHostile < 3 || currentSituation.seriousHostile) && currentSituation.bulletsLeft > 0){
            // Shoot! (And possibly create opening!)
            PlayerId[][] visibleBoard = context.getPlayField();

            if (debug) System.out.println("Shooting!");

            return new Shoot(visibleBoard[currentSituation.nearestHostile.x-2][currentSituation.nearestHostile.y-2]);
        }

        if (currentSituation.hostiles > 6) {
            // Code red: get out of here! Trample over hostiles if necessary.
            // Guarantee path will generate, without hitting anything dead.
            currentSituation.pathScore = -9000;
        }

        findSafePath(currentSituation);

        Coordinate next = currentSituation.path.get(0);

        if (next.x == 10 && next.y == 10){
            if (debug) System.out.println("Staying Put.");
            return Move.STAY;
        }

        if (debug) System.out.println("Moving!");

        return Move.inDirection(next.x-2, next.y-2);
    }

    private void findSafePath(Situation currentSituation) {
        int x = 10;
        int y = 10;

        // Since we have a finite number of tiles, and we won't consider
        // backtracking, Let's consider every possible path to optimize the
        // safest path.

        current_iterations = 0;

        pathIteration(currentSituation, new ArrayList<Coordinate>(), x, y, 0);
    }

    private void pathIteration(Situation s, ArrayList<Coordinate> currentPath, int x, int y, int steps) {
        // If we've reached an end state,
        // Update situation if the currentPath has a higher (less negative) score than the current path.
        // As well as if we moved the minimum amount

        // Compute Score
        int score = 0;
        for (Coordinate c : currentPath) {
            score += s.scores[c.x][c.y];
        }

        int distanceTraveled = (Math.abs(10 - x) + Math.abs(10 - y));

        // Return if the currentPath has a lower score than the current path.
        if (score < s.pathScore || s.pathScore == 0 || current_iterations > max_iterations) return;

        if (debug) System.out.println("debug: step " + steps + " (" + score + " : " + s.pathScore + ") Distance: " + distanceTraveled);

        // Prevent my algorithm from blowing up the whole works
        current_iterations++;

        if (steps == pathLength) {
            if (distanceTraveled >= minPathDistance) {
                if (score > s.pathScore) {
                    s.path = currentPath;
                    s.pathScore = score;
                }
            }

            return;
        }

        ArrayList<Coordinate> searched = new ArrayList<Coordinate>();
        for (int index = 0; index < 9; index++){
            int minx = 0, miny = 0;
            int minscore = -1000;

            for (int i = -1; i < 2; i++) {
                for (int j = -1; j < 2; j++) {
                    if (searched.contains(new Coordinate(x+i, y+j)) || currentPath.contains(new Coordinate(x+i, y+j))){
                        continue;
                    }

                    if (steps > 1){
                        Coordinate c0 = currentPath.get(steps-2);
                        Coordinate c1 = currentPath.get(steps-1);

                        int dx = c1.x-c0.x;
                        int dy = c1.y-c0.y;

                        // Disable turning more than 45 degrees
                        if (dy != j && dx != i) continue;
                    }

                    if (s.scores[x+i][y+j] > minscore){
                        minx = x+i;
                        miny = y+j;
                        minscore = s.scores[x+i][y+j];
                    }
                }
            }

            if (!currentPath.contains(new Coordinate(minx, miny))) {
                ArrayList<Coordinate> newPath = (ArrayList<Coordinate>) currentPath.clone();
                newPath.add(new Coordinate(minx, miny));
                pathIteration(s, newPath, minx, miny, steps + 1);
            }

            searched.add(new Coordinate(minx, miny));
        }
    }

    private void hostileDetected(Situation seriousSituation, int bx, int by, PlayerContext context) {
        boolean verySerious = false;
        if (this.isHostile(context.getPlayField()[bx][by].getName(), true) && context.shootablePlayers().contains(context.getPlayField()[bx][by])){
            seriousSituation.seriousHostile = true;
            verySerious = true;
        }

        for (int i = -4; i < 5; i++) {
            for (int j = -4; j < 5; j++) {
                // Prevent from overflowing the path matrix.
                if (i + bx + 2 < 0 || i + bx + 2 > 20 || j + by + 2 < 0 || j + by + 2 > 20) continue;

                int separationLevels = Math.max(Math.abs(i), Math.abs(j));
                seriousSituation.scores[bx + i + 2][by + j + 2] += separationLevels*2 - 10;
            }
        }

        int distanceToHostile = Math.abs(10 - (bx + 2)) + Math.abs(10 - (by + 2));
        if ((distanceToHostile < seriousSituation.distanceToHostile && !seriousSituation.seriousHostile) || verySerious){
            seriousSituation.nearestHostile = new Coordinate(bx + 2, by + 2);
            seriousSituation.distanceToHostile = distanceToHostile;
        }
    }

    private void friendlyDetected(Situation lessBleakSituation, int bx, int by) {
        for (int i = -4; i < 5; i++) {
            for (int j = -4; j < 5; j++) {
                // Prevent overflowing the path matrix.
                if (i + bx < 0 || i + bx > 20 || j + by < 0 || j + by > 20) continue;

                int separationLevels = Math.max(Math.abs(i), Math.abs(j));
                lessBleakSituation.scores[bx + i + 2][by + j + 2] += 4 - separationLevels;
            }
        }
    }

    private boolean isHostile(String name, boolean serious){
        // Generated from a list of players who shot me during testing.
        // If anyone adds me as a 'friendly', I'd be happy to reciprocate.
        switch(name){
            case "Bee":
            case "Coward":
            case "Fox":
            case "Gunner":
            case "HideyTwitchy":
            case "Sokie":
            case "ZombieHater":
            case "ZombieRightsActivist":
                return true;
            default:
                return (!serious && name.equals("Zombie")); // Zombies don't shoot
        }
    }
}

Tyzoid

Posted 2014-07-20T19:48:18.813

Reputation: 692

I'm afraid you're a little too late to be in with a chance of winning, but if I get chance later, I'll do one more run with your entry included, to see how you fare. – James_pic – 2014-08-04T09:27:01.713

Yeah, I judged my submission based on the timeout of the bounty :/ - I don't expect to be close to wining though... the bot isn't very good. – Tyzoid – 2014-08-04T13:19:16.370

1Sorry about that, the bounty was set to timeout the day after the stated due date of August 3rd so I could award it today, August 4th. (that being said you shoot everyone who shoots either of my characters so I should be rooting for Tyzoid.) – kaine – 2014-08-04T18:18:49.693