Збережіть останню кулю для себе


51

Зараз конкурс закінчився. Трус - переможець. Останній матч ви можете подивитися тут .

Раптом з’являються зомбі! О НІ НЕ!

У цьому виклику короля-гірки ви повинні створити бота, щоб пережити апокаліпсис зомбі. Або, принаймні, триматися якомога довше.

На початку гри 50 екземплярів кожного запису будуть розміщені випадковим чином у великій тороїдальній ігровій зоні - тобто вона, здається, квадратна, але обертається навколо. Розмір ігрової зони буде змінюватися в залежності від кількості записів, але спочатку 6% квадратів будуть зайняті. Кожен учасник стартує з 3-х куль.

На початку кожного повороту зомбі підніметься із землі у випадковому місці, знищуючи все, що було над ним. Будь-який гравець, який знаходиться поруч із зомбі на початку своєї черги, стане зомбі.

Кожен живий гравець потім буде називати їх код. Він отримає об’єкт PlayerContext , що містить інформацію про їх поточний стан та оточення. Кожен гравець може побачити по 8 квадратів у будь-якому напрямку.

Гравець повинен вибрати або рухатися (залишатися нерухомим - це дійсний рух), повертаючись Move, або стріляти поруч із людиною чи зомбі, повертаючи а Shoot. Ваш пістолет має максимальний діапазон 5 квадратів. Оскільки ви знаходитесь в зоні дії гармати, ви можете стріляти самостійно, якщо у вас залишилися кулі. Якщо двоє гравців стріляють один в одного, вони обоє гинуть.

Якщо два гравці намагаються просуватися на одну і ту ж площу, вони зазнають невдачі, і обидва повернуться на площу, з якої почали. Якщо конфлікти все ще будуть, це буде повторюватися, поки не виникнуть конфлікти, що може означати, що всі повернулися з місця, де вони почалися.

Якщо гравець загине від вогнепального пострілу, їх мертве тіло залишиться і утворює постійний бар'єр. Будь-які кулі, які вони носили, залишаються на їх особі, і гравці можуть бути відлякані на сусідніх площах. Якщо на площах, прилеглих до мертвого тіла, є кілька гравців, то кулі поділяться між ними, але будь-яка інша частина буде втрачена.

Якщо гравець стає зомбі, то їхні кулі втрачаються. Зомбі бездумно йдуть до найближчого живого гравця.

Записи підраховуються тим, як довго виживає їхній гравець, який вижив найдовше.

Записи

Програма контролю доступна на веб- сайті https://github.com/jamespic/zombies . Просто клонуйте його і запустіть mvn compile exec:java.

Щоб мати право на участь, записи повинні бути написані мовою JVM, повинні бути портативними і повинні бути можливі для створення з Maven без спеціальних налаштувань. Це робиться для того, щоб конкурентам не потрібно встановлювати декілька середовищ виконання, щоб перевірити своїх ботів на конкурентів.

Зразки записів доступні на наступних мовах:

Якщо ви хочете змагатись з мовою, яка не вказана в списку, ви можете опублікувати коментар із запитом, і я досліджую можливість інтеграції обраної вами мови в контрольну програму. Або, якщо ви нетерплячі, можете подати запит на тягу до програми управління.

Для кожного запису буде створено лише один екземпляр (у значенні Java). Цей екземпляр Java буде називатися декілька разів за поворот - один раз для кожного гравця, що вижив.

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

Додаткові правила

Кожен запис повинен мати унікальну назву, щоб правильно працювати з програмою управління.

Записи не повинні намагатися підробити інших учасників, або за допомогою програми управління, або іншим чином скористатися умовами виконання, щоб "зламати четверту стіну", і отримати перевагу, яка не була б доступною в "справжньому" апокаліпсисі зомбі .

Спілкування між гравцями дозволено.

Переможець - абітурієнт, чий бот має найвищий бал у тесті, який я проведу 3 серпня 2014 року.

Кінцеві результати

Остаточні результати є! Трус - переможець!

2 серпня я провів 19 раундів контрольної програми та класифікував кожного гравця відповідно до їх медіанної оцінки. Результати були такими:

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

Останній тур можна переглянути тут .

Результати запуску

Індивідуальні результати кожної з 19 серій були:

#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

1
@Pureferret Код Frege складається з файлу Frege, що містить прив’язки на github.com/jamespic/zombies/blob/master/src/main/frege-bindings/… , та класу помічників Java, який закликає Frege на github.com/jamespic / зомбі / blob / master / src / main / java / zombie /… . Якщо ви можете клонувати РЕПО (або завантажити його як поштовий файл з github.com/jamespic/zombies/archive/master.zip ), Maven обробляє збірку для вас.
James_pic

1
@Pureferret Спроба налаштувати проект вручну буде болючою . Ніхто їх ще не використовував, але є компілятори та перекладачі на півдесятка мов. Рефлексія дозволена (дійсно, деякі динамічні мови не можуть працювати без неї), доки вони не використовуються для підбору гри або ваших конкурентів. Подивіться на відслідковування байового тренда на прикладі "спілкування".
James_pic

1
@sokie Це звучить нормально - деякі записи вже роблять щось подібне, і я зрозумів, коли ставив перед собою завдання, що деякі абітурієнти хочуть, щоб їх боти десь переглянулися або обмінялися інформацією про їх оточення. Ми скажемо, що у гравців є уокі-токі.
James_pic

2
@James_pic Я додав цей код до своєї локальної копії ( pastebin.com/PutPn9ff ) у Game.java, щоб я міг використовувати клавіші зі стрілками для руху вперед та назад у грі. Думав, що вам може бути корисно додати
Moop

2
Невелике розчарування, що переможець дозволив собі зазнати певної зомбіфікації, а не скоїти самогубство.
Спарр

Відповіді:


15

Трус

Правила боягузтво.

  1. Якщо ви не можете втекти, панікуйте і знімайте все, чого не знаєте.
  2. Біжи !!!
  3. Під час бігу ви також можете забрати кілька куль. Глибоко вниз ви знаєте, що не можете бігти назавжди.
  4. Бігаючи, шукайте інших боягузів. Нещастя любить компанію. І вони можуть спочатку з'їсти іншого хлопця.
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));
    }
}

Я радий спробувати це. Усі записи до цього часу були зосереджені на боротьбі, але ніхто не намагався бігти чи ховатися.
James_pic

О, і вам не потрібно слідкувати за тим, що ви вбили. Він відображатиметься як DeadBodyінший ідентифікатор, а не як раніше.
James_pic

1
Це не для того, щоб відслідковувати, це для того, щоб не знімати одну і ту ж річ двічі за один і той же раунд.
Тайлон

1
Внесло кілька змін, найкращий запуск з епічним розбором
Тайлон

1
@Thaylon Відформатував останній вихідний код для вас. pastebin.com/4WDb6s8C
HuddleWolf

44

Емо вовк із пістолетом

Він повернувся . Він ненавидить зомбі. Він досі ненавидить Java. Не передбачається порушення авторських прав.

package player;

import zombie.*;

public class EmoWolfWithAGun implements Player {

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

}

3
+1 Робить більше стін для гаманців ...
Moop

13

Активіст із прав зомбі

Рух «Права зомбі» швидко набув популярності під час компенсації апокаліпсису. Ідея вбити кожного зомбі на очах без каяття для них абсолютно жорстока, тому вони стріляють в інших гравців, які не вірять у справу. Розуміючи боротьбу, вони обіймуть зомбі, якщо ворогів не буде на очах.

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

}

12

HuddleWolf - Java

Правило 8 : Подорож у групах

HuddleWolf приймає до серця шосте правило Зомбіленду. Він буде гнатись і тулитися з будь-яким неприязнім об’єктом, який він бачить. Якщо HuddleWolf не побачить, з ким можна тулитися, він вирушить на північний схід у пошуках більш населених районів. HuddleWolf також ненавидить зомбі і стрілятиме на видовище.

HuddleWolf зрозумів, що "Трус" набагато краще реалізує свою первісну ідею. Він поклоняється верховенству боягуза і зараз активно віддає перевагу компанії трусів перед іншими непристрастями.

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: Зараз вбиває артилеристи
HuddleWolf

Ви також можете додати && !(playerAtLocation.equals(context.getId()))до своїх умов поспішання. Зараз він рухається до найближчого гравця - крім найближчого гравця ви, тому він залишається нерухомим.
James_pic

Ви маєте на увазі правило 8 ....?
Pureferret

@Pureferret: Ти маєш рацію. Моїм оригінальним натхненням був не гармат Zombieland.
HuddleWolf

FYI дивлюся на ZombieHater. Я не думаю, що він насправді ненавидить зомбі. Він просто розстрілює всіх нас, як навідник. Ймовірно, ви повинні додати його до списку обов'язкових цілей.
kaine

11

Лисиця

Лисиці потрібна лисиця.

Використовує добру частину мого боягуза, але дотримується іншої стратегії. Якщо ви вирішите прийняти (під) місію, лисиця вирішить побудувати фокс.

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

Мені це подобається. Я переживаю, що встановлю небезпечний прецедент, дозволю декілька записів (деякі більш агресивні записи, можливо, зможуть отримати позицію, додавши прихильників), але ця запис, очевидно, не допомагає Трусу (якби хтось ще написав це, вони, мабуть, зробили б більш-менш те саме), тому я дозволю це. Я повинен отримати шанс перевірити це завтра.
James_pic

Додано кращий рахунок (більше не стоїть нерухомо через зв’язки) та відзначає більше DeadBodys як розграбованих. Хотілося б спробувати кращий підрахунок загрози, але процес тестування зараз займає занадто багато часу, коли трус випадково працює до 15000 патронів. Це, мабуть, в основному пов’язано з більшими ігровими умовами, а отже, і зниженими шансами стати нерестовим зомбі. Найкращою стратегією, здається, є: зібрати якомога більше гравців до 1000 круглих, перестати рухатися і тоді нехай удача вирішить.
Тайлон

10

Waller - Java

Уоллер любить стіни і прагне їх сховатися від зомбі. В ідеалі, Уоллер дуже хотів би опинитися в стіні і почекати апокаліпсису.

Ідеальна стіна

Ідеальною стіною є те, де Валлер завершений оточенням стін:

   DDD 
   DWD 
   DDD

Оскільки єдиний спосіб померти - це застрелити або нерести зомбі під собою чи сусідніми. Це найкращі шанси уникнути їх.

До ідеальної стіни важко підійти, але Уоллер знайде найкраще можливе місце і чекатиме зомбі чи інших гравців до нього, щоб стріляти і розширювати його стіну.


Алгоритм порівняно простий

  1. Чи зомбі збирається кусати? стріляти в них.
  2. Знайдіть найкраще розташування стіни (оцінено 0 - 8) у полі зору
  3. Знайдіть найкоротший шлях до цього місця та біжіть до нього!
  4. Спробуйте збільшити стіну
  5. Зачекайте ...

Це незавершена робота, не соромтесь взяти все, що я написав, щоб використати для себе. Я написав простий алгоритм A *, щоб знайти найкращий шлях до потрібного місця з урахуванням стін та інших гравців. Він перераховується кожен раунд, оскільки стіни, можливо, змінилися між ними.


Журнал змін:

  • Намагався покращити ефективність ранньої гри, чекаючи вихід та уникаючи агресивних гравців протягом перших кількох витків перед тим, як знайти / будувати стіни.

  • Додано ваги до пошуку шляху, щоб взяти найкращий маршрут з точки зору відстані та позиційного бала. Зараз частіше грабуватимуть і, сподіваємось, кулі не закінчаться. Покращена відкриваюча гра, тікаючи від агресивних гравців.

  • Виправлені проблеми із знаходженням контуру на зайнятій точці

  • Далі очищений код. Додано більше стін, щоб зайняти позицію, ніж лише сусідні стіни. Розширено, наскільки далеко Уоллер розширить свою стіну.

  • Трохи очищений код. Реалізовано реєстр зйомок, щоб уникнути, коли два гравці стріляли в одного гравця під час того ж повороту (натхненний Тайлон)

  • Додано пошук шляху між найближчими зомбі та поточним Walelr. Уоллер буде стріляти в зомбі лише в тому випадку, якщо він зможе дістатися до нього за певну кількість рухів. Це, сподіваємось, вбереже кілька куль, коли їх стіна перекриє шлях зомбі.


Випуски

  • Уолер може бути в хорошому положенні, але бачить краще розташування стін. Вони безглуздо пробігатимуть через землю, заражену зомбі, щоб дістатися до цього нового місця. (Мені потрібно відмовити це)

  • Рання гра є грубою для Уоллера, неподалік фортифікація поруч і багато агресивних гравців. (Мені потрібно покращити першочергове виконання гри)

  • Немає зв'язку між Валлерами в одних і тих же місцях. Потрібно, щоб вони працювали разом, щоб побудувати найкращу стіну.


Ось код, я не програміст Java (C #), тож пробачте мої помилки java.

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

О, це приємно. Я сподівався, що хтось щось зробить із алгоритмом пошуку справжнього шляху - я трохи розгулювався з Dijkstra, але, схоже, це може зробити мою роботу застарілою.
James_pic

@James_pic Спасибі, я сьогодні багато грав з ним. Багато цікавого, що потрібно зробити, коли у вас є пошук шляху. Мої початкові випробування поставили його на третє місце після боягуза та стрільця.
Moop

Не те, що з цим можна нічого не робити, але фей, зомбі можуть нерестуватися не тільки на порожніх полях чи гравцях, але й під загрозою DeadBody.
Тайлон

@Thaylon Я помітив, що переглядаючи вихідні карти. Нічого, до чого я не можу підготуватися, але дякую, що доглядав за мною.
Moop

Можливо, помилково ставитися до StandStill як до стіни. Звичайно, він не рухається, але це захист проти зомбі
James_pic

8

Гордон Фріман

Гордон Фріман ненавидить зомбі, тому він ніколи не вбиватиметься, але у нього немає ніяких труднощів з тим, щоб шукати більше патронів, щоб стріляти в більше зомбі.

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

Я сподіваюся, що ви не заперечуєте, але я вніс декілька незначних змін у ваш код, щоб змусити його складатись, і виправив пару помилок з покажчиком чи покажчиком нульового масиву, які робили помилку під час виконання. Я не змінив логіку.
James_pic

@James_pic: Я ціную виправлення, я, щонайменше, не програміст Java, хоча мені подобається думати, що я досить добре працюю в цих завданнях KotH: D.
Кайл Канос

Ви можете подивитися перегляд останнього матчу за адресою jamespic.github.io/zombies/2014-07-21/0.html . Схоже, доктор Фріман завжди ходить на північний захід, що може пояснити його непослідовність.
James_pic

@James_pic: Хто з них Фрімен? Є два G (один синій, другий жовтий). Якщо мій - це синій G, то, здається, він рухається випадковим чином, а не лише СЗ. Також, чи є спосіб сказати між мертвим зомбі та мертвою людиною?
Кайл Канос

1
Дякую :) Я люблю повторно використовувати код, коли можу, але я вважаю за краще визнати, коли він не мій.
sokie

7

Священик

Якщо у вас є віра, вам не потрібно бігати і не стріляти.

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

4
HuddleWolf.... Я бачу, що ти там робив;)
Кайл Канос

Боюся, вам потрібно буде назвати свій клас чимось іншим HuddleWolf, як це ім’я вже прийнято
James_pic

6

ZombieHater

Це подання ненавидить зомбі! Він намагається стріляти в найближчого зомбі, поки у нього є кулі, потім збирає більше куль, щоб вбити більше зомбі.

Редагувати: ZombieHater зараз не соромиться вбивати інших людей, щоб отримати більше куль. Він також виявляє перешкоди і намагається обійти їх.

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

-1. Незважаючи на те, що це стосується виконання, це майже те саме, що і мій Гордон Фрімен ....
Кайл Канос

2
@KyleKanos. Це та сама ідея, але враховуються деталі реалізації. Наприклад, відстеження того, які трупи мають награбоване, є інновацією.
James_pic

Так, і на предмет відстеження лута, PlayerIdмає відповідну реалізацію equalsі hashCode, і залишається незмінним в протягом «життя» гравця (вона змінюється тільки тоді , коли вони помирають або поворот), так що ви можете знайти його простіше тримати PlayerIdз в emptyDeadBodiesчому абсолютна позиції.
James_pic

@KyleKanos я ніколи не збирався копіювати вашу логіку. Все, що я зробив, - це створити власного бота так, як я думаю, що це буде найкраще.
CommonGuy

1
Гарні модники! Я починав хвилюватися, що більшість стратегій в основному однакові, але ця рандомізація руху дала вам величезний імпульс!
James_pic

6

Vortigaunt

Завжди буде слідувати за доктором Гордоном Фріманом, або ходитиме безцільно, якщо він не в одному вимірі.

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

}

Отже, ви ніколи не стріляєте ... але тримайтеся поруч із тим, хто це робить ... Я не можу сказати, чи це вб'є Фрімана, але це буде приголомшливо для населення зомбі ...
kaine

6

Кокон - Фреге

Який сором. На півтора десятка мов на вибір, і кожен просто використовує Java. Ну, не має сенсу відпускати їх на сміття, тож ось конкурент у Фреге.

Він використовує алгоритм Дейкстри, щоб знайти відокремлене місце, щоб вичекати апокаліпсис, йде на пошуки куль, якщо він закінчиться, і стріляє зомбі, якщо вони занадто близько.

Оновлено

Cocoon зараз ігнорує маршрути, які могли б прийняти його в межах відстані зомбі в алгоритмі маршрутизації, і стріляє зомбі, коли вони знаходяться в межах 2 квадратів, а не 3 (для створення більш міцного кокона).

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

4

Навідник - Java

Ось приклад, як можна позбутися від блоків. Він стріляє все, що бачить, або блукає безцільно, якщо навколо нічого немає, або в нього немає куль.

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

}

Ви можете зробити щось краще?


3

HideyTwitchy

Горіх, який ховається від усього, якщо тільки гравець не переслідує його до стрільбища, і в цьому випадку він панікує і стріляє (включаючи свій власний вид). Тільки грабує тіла, якщо він без боєприпасів, то відбирає трупи.

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

3

SuperCoward - JAVA Я знаю про подвійне подання, але я просто не втримався. Скажіть, будь ласка, чи варто його видаляти.

Що за боягуз, той, кого стріляє і воює? Представляючи вам СУПЕР Труса, він біжить на поле, намагаючись уникнути того, хто вважає ворогами та зомбі. Він намагається залишатися в безпеці і уникає перешкод. Якщо він не знайде хорошого маршруту, панікує і залишається на місці

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

Моє теперішнє мислення полягає в тому, що я дозволятиму подвійні подання до тих пір, поки між ними не буде змови - так що записи, які так само легко можуть бути подані різними гравцями, в порядку, але прихильники чи багдоки - ні. Це виглядає так, що має бути нормально (якщо хтось не заперечує), тому я перевірю це, коли отримаю шанс.
James_pic

3
+1 заDANGER danger = DANGER.DANGER;
Андрей Костирка

3

Стрілець

Я визнаю, що моя головна мета - стріляти з артилериста.

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

    }

}

@Benny, як це зробити, щоб більше не потрібно редагувати?
kaine

На сьогодні це не компілюється. Я хотів виправити це, але є кілька місць, де я не можу зрозуміти, який був початковий намір коду (наприклад, компілятор скаржиться на те, що TargetZombieвін не може бути ініціалізований, і я не впевнений, що ви хочете зробити). Ви хочете зробити ще один розмах?
James_pic

Дякую за спробу. Я не встановив ваш код, тому він не вдався, оскільки він не розпізнав ваші константи. Я не пам’ятав, що він не складеться, якби у TargetZombie не було значення за замовчуванням (хоча воно ніколи не використовувалось, якби не завдяки константі зомбі). Я змінив його, щоб він вбивав мене, якщо я помиляюся на цьому !. Не витрачайте часу на спробу редагування, якщо вам не нудно, я напишу ще один коментар, якщо зможу підтвердити, що він працює поряд з іншими гравцями.
kaine

Я виправив це, щоб він компілювався і запускався без помилок - здебільшого «вимикаючи CENTRE_OF_VISION» помилки та деякі NPE. Вам доведеться поглянути на пробіг ( jamespic.github.io/zombies/2014-07-23/0.html ), щоб побачити, чи це робить те, що ви очікуєте.
James_pic

Схоже, ви мали великий вплив на тривалість життя артилеристів! Цікаво, чи це спричинить хвилю кращих конкурентів, які виймають один одного?
James_pic

2

Сокі - JAVA

Сокі знає, що вам краще в пачках, тому він намагається перейти до найближчого союзника, якого він знайде. Під час руху, якщо він загрожує небезпекою, намагається битися або іншим чином намагається бігти. Коли він дістається до друзів, всі вони б’ються разом, поки у них немає боєприпасів, а потім намагаються знайти найближчі тіла, щоб їх очистити.

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

}

FYI, ім'я вашого гравця буде таким же, як і назва вашого класу (в даному випадку "Sokie"). Тож я підозрюю, що, playerAtLocation.getName().equals("BetterInPacks2")мабуть, має бути playerAtLocation.getName().equals("Sokie").
James_pic

Данг! Це був якийсь залишковий код, коли я тестував різні реалізації, я виправлю його за короткий час! Дякую
sokie

2

Бджола - пітон

Другий запис, але я подумав, що було б цікаво спробувати щось інше та іншою мовою.

  • Тепер Бджоли уникати переїзду на те саме місце.
  • Більше "Pythonic" зараз
  • Оптимізовано переміщення тору, щоб бджоли могли швидше дістатися до королеви (дякую Джеймсу, що дозволив отримати доступ до розміру дошки)

Бджоли вважають за краще бути разом, тому вони позначають одну з уцілілих бджіл як бджолу-королеву і припливають до неї. Вони будуть жалити опонентів на шляху до неї, віддаючи перевагу плоті зомбі перед людською.

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

1
Фу! Я починав хвилюватися, що на цьому конкурсі не буде нічого, крім Java.
James_pic

Так, і перший запис, який шукає поле в усьому світі, що, на мою думку, є акуратною особливістю.
Moop

Одне, що може бути вам корисним, - це те, що Jython автоматично надає геттери та сетери для JavaBeans як властивості. Наприклад, ви можете використовувати, context.gameClockа не context.getGameClock().
James_pic

@James_pic Дякую за пораду, не знав цього.
Moop

2

Містер Асассін

Його батьки не дуже милі люди. Він не має жодного доброго вбивства, але не буде, якщо думає, що ти допоможеш йому жити довше. Він поводиться з усіма так, ніби вони особливі, а потім відкидає це і класифікує вас за тим, наскільки близький він хоче вас, наскільки він хоче вас мертвих, і ви важливі для поточної ситуації.

Як я знаю, багато цього приховано тут, я поясню деякі. Зазвичай він ніколи не вбиває хлопців, які можуть вбити його погрози, перш ніж йому доведеться, оскільки вони не є його цілями. Він нагодує тих, хто ніколи не стріляє. За одним винятком, він вбиває вас, якщо ви в даний час його вбите. Він вважає за краще огороджувати стіни в тих місцях, не примушуючи їх вводити код. Він Фокс фобій і боягуз. Він чіпляється до Хаддлволв і стіни з Уоллером. В даний час він рухається в бік групувальників (як Сокіє, хоча Сокіє нещадно стріляє в нього). Неш повинен любити його, як його пріоритети плачуть теоретиком гри. Однак його клієнти здаються дуже зенофобними, тому що він вбиває священиків, прибульців та приїжджих (хоч він помилував вас у п’ятницю або в суботу, якщо ви не вб'єте його ... неділя занадто пізно). Вибачте, якщо я забуду когось іншого.

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

@James_pic Це спрацювало в грі (хоча попередню версію я клонував кілька днів тому). Це має працювати. Якщо це не дає мені знати. Якщо він не відбудеться в неділю, видаліть його.
kaine

Здається, це працює в поточній версії коду. Я включу його в тест пізніше сьогодні.
James_pic

Знову ж таки, я не маю уявлення, як працює цей запис. Якби хтось подав це, я вважаю, що це була нерозумна нісенітниця, але оскільки Shotguneer в даний час б’є моє найкраще вступ, мені по-справжньому цікаво подивитися, що це насправді робить.
James_pic

Досить складне і, я припускаю, управління загрозами / дистанцією / пріоритетом.
Тайлон

1

SOS (зйомка на прицілі)

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

1

Джон Нэш - Javascript

Рішення про те, чи розстріляти когось, по суті є дилемою в'язня. Якщо ви припускаєте, що ваш супротивник уже вирішив свою думку, то найкращий варіант - це завжди стріляти в них. Однак якщо ваш супротивник вирішить, що робити, виходячи з того, що вони думають, що ви зробите, то найкращий варіант - залишити їх, і вони, ймовірно, зроблять те саме.

Джон Неш стріляє лише «грудками» - супротивниками, які вже склали свою думку. Тобто він стріляє противників, які завжди стріляють, або противників, які ніколи не стріляють. Він залишає супротивників у спокої, якщо у них складніша логіка.

Коли він не стріляє, він шукає кулі або прямує на південь.

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

Чи MoveRandom випадково робить щось, крім переміщення випадковим чином?
Тайлон

@Thaylon Nope, він робить саме те, що написано на жерсті. Moop включив його у запит на витяг, який він надіслав проекту. Основна мета запиту на виклик полягала в поліпшенні виводу HTML, але також включала MoveRandomly, і я подумав, що я включу MoveRandomly в тест, щоб побачити, що відбувається в тесті.
James_pic

Я витягнув останнє з вашого github і не зміг змусити компілювати john-nash.js
Moop

@moop Я знайшов те саме.
Pureferret

Виправлено це. Я протестував його лише на Java 7. Він повинен працювати і в 8, зараз
James_pic

1

SunTzu намагається бути тактичним і знаходити безпечні місця в сітці, до яких можна рухатися. Але на даний момент він просто працює на басейні.

«Таким чином, ми можемо знати, що для перемоги є п’ять найважливіших:
1. Він виграє, хто знає, коли боротися, а коли не битися.
2. Він виграє той, хто вміє поводитися і з вищими, і з нижчими силами.
3. Він переможе, чия армія оживлена ​​тим самим духом у всіх її лавах.
4. Він переможе, хто, підготувавшись, чекає, щоб взяти ворога непідготовленим.
5. Він виграє того, хто має військовий потенціал і не заважає суверен. "

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

    }

Поточні проблеми полягають у тому, що зона небезпеки не створена належним чином, і я не думаю, що найбільші загрози заповнені правильно.


+1 за використання класів від Guava. Я забув, що я використовував це в програмі управління, але в ньому є багато корисних класів саме для подібних речей.
James_pic

1
@james_pic tbh, я б розпродався за еквівалент Apache. Відчуваю, що я це зробив.
Pureferret

Оскільки він побудований з Maven, я можу додати будь-які розумні залежності, які вам можуть захотіти, якщо це полегшить ситуацію. Якщо ви хочете використовувати Commons Collection, перейдіть до цього.
James_pic

Річ у тому, що @james_pic я ледве змусив це будувати з Maven, оскільки я насправді цього не розумію ... Я б краще попрацював з тим, що мають інші, але зробити більше.
Pureferret

1

Тизоїд - мій дещо тугий робот

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

Я боюся, що ви трохи пізно затримаєтесь, щоб мати шанс на перемогу, але якщо я отримаю шанс пізніше, я зроблю ще один запуск з вашим входом, щоб побачити, як ви проїдетесь.
James_pic

Так, я оцінив моє подання, виходячи з тайм-ауту виграшу: / - Я не сподіваюся, що я буду близький до виграшу, хоча ... бот не дуже хороший.
Тизоїд

1
Вибачте з цього приводу, бонус був встановлений на тайм-аут наступного дня після зазначеної дати погашення 3 серпня, щоб я міг нагородити його сьогодні, 4 серпня. (що, як кажуть, ти знімаєш усіх, хто стріляє в будь-якого з моїх персонажів, тому я повинен вкорінюватися за Тизоїда.)
kaine
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.