Codémon, wybieram cię!


55

Twój uprzejmy sąsiad, Doktor Tree, właśnie dał ci trzy magiczne stworzenia zwane Codémon. W pobliskim mieście Colorville odbywa się turniej bitewny. Czy jesteś najlepszy, jak nikt przedtem?

Przegląd

To jest turniej bitewny. Każdy gracz kontroluje zespół trzech potworów, a jego celem jest znokautowanie (zabicie) drugiej drużyny. Jest 100 rund, a punkty są przyznawane za wygrane i remisy. Drużyna z największą liczbą punktów wygrywa!

Potwory

Codémon to skomplikowane małe stworzenie. Do wyboru jest pięć rodzajów (elementów), trzy statystyki i trzy miejsca na ruch.

Rodzaje

Do każdego Codémon przypisany jest jeden typ. Pięć typów to Normalny, Wróżki, Ogień, Woda i Trawa. Każdy ma swoje mocne i słabe strony. Obrażenia są oparte na poniższej tabeli:

tabela typów

Liczby są mnożnikami obrażeń. Na przykład Woda atakująca Ogień ma modyfikator 0,5 (połowa obrażeń), natomiast Trawa atakująca Ogień jest podwojona (2).

Statystyki

Każdy potwór ma trzy statystyki, które określają jego zdolności bojowe. Atak podnosi obrażenia, które wyrządza. Obrona obniża otrzymywane obrażenia. Prędkość pozwala mu poruszać się przed tymi o niższej prędkości.

Każdy potwór ma wartość początkową 50 dla każdej statystyki i maksymalnie 100. Podczas tworzenia potworów będziesz w stanie przypisać 80 dodatkowych punktów statystyk (każdy). Pamiętaj, że żadna indywidualna statystyka nie może przekroczyć 100. Tak więc możesz mieć dystrybucję 100/80/50, 90/80/60 lub 65/65/100, ale 120/50/60 jest nielegalne. Każda drużyna z nielegalnymi statystykami jest zdyskwalifikowana. Nie są wymagane , aby korzystać z wszystkich 80 punktów, ale prawdopodobnie nie powinien iść z minimalną 50/50/50.

Możesz również uznać HP za statystykę, ale każdy Codémon ma niemodyfikowalne 100 HP. Kiedy HP spadnie do zera, nie mogą kontynuować walki. Punkty życia są uzupełniane do 100 przed każdą bitwą.

Rusza się

Każdy potwór zna trzy ruchy bojowe. Wszystkie trzy wybrane muszą być odrębne, aby nie było opcji Punch / Punch / Punch.

Istnieje 15 ruchów, po trzy z każdego rodzaju. Każdy typ ma atak bezpośredni, słabszy atak z efektem i ruch pojedynczego efektu.

id  name        type    power   uses    usable  effect

0   Punch       N       20      -       NFWG
1   Heal        N        0      3       NFWG    Heals 50 HP
2   Slow        N       10      5       NFWG    Enemy speed x0.8
3   Pain        P       20      -       PFWG
4   Sleep       P        0      3       PFWG    No enemy action until wake
5   Weaken      P       10      5       PFWG    Enemy Atk x0.8
6   Fireball    F       20      -       NPFW
7   Burn        F        0      3       NPFW    Enemy -10 HP each turn
8   Sharpen     F       10      5       NPFW    Own Atk x1.25
9   Watergun    W       20      -       NPWG    
10  Confuse     W        0      3       NPWG    Enemy may strike itself (10 power)
11  Shield      W       10      5       NPWG    Own Def x1.25
12  Vine        G       20      -       NPFG
13  Poison      G        0      3       NPFG    Enemy -5xTurns HP each turn
14  Sap         G       10      5       NPFG    Enemy Def x0.8

typeodnosi się do rodzaju ruchu. powerjest jego uderzająca moc. useswskazuje, ile razy można go użyć w bitwie ( -jest nieograniczony). usablepokazuje, jakich typów może używać (na przykład Puncha nie można podać typowi Psychicznemu, ponieważ nie ma go P). effectpokazuje, jakie efekty mają ruchy. Istnieje 75% szans na zadziałanie każdego efektu, z wyjątkiem Leczenia, które zawsze działa.

W przypadku efektów, które zmieniają statystyki potwora, efekty można nakładać . Na przykład dwukrotne użycie Osłabienia może obniżyć atak przeciwnika do skuteczności 0,64. Efekty, które nie zmieniają statystyk potwora (Sleep, Burn, itp.) Nie nakładają się .

Sen usypia przeciwnika, z 60% szansą na przebudzenie na początku każdej tury. Śpiące potwory nie podejmą żadnych działań.

Podpalenie zadaje przeciwnikowi 10 punktów życia na koniec każdej tury, gdy jest aktywny . Trucizna działa podobnie, ale z każdą turą wzrasta jej ilość. W pierwszej turze jest 5, a następnie zyskuje 5 w każdej turze. Tak więc, do czwartej tury, będzie to zadawać obrażenia za 20. Są to obrażenia płaskie, nie zależne od typu potwora ani nie podlegające bonusom.

Zamieszanie może spowodować sam atak potwora zamiast wykonania tego, co mu nakazano. Ten atak ma moc 10 i ma 30% szansy na wystąpienie w danej turze.

Żeby było jasne, efekty trwają do końca bitwy (oprócz Sen, jak wspomniano powyżej).

Ruchy również otrzymują 20% wzmocnienia mocy, jeśli są używane przez potwora odpowiedniego typu. Na przykład, Trawiasty potwór korzystający z Winorośli zostaje wzmocniony, podczas gdy nie używa Puncha.

Tajne statystyki

Statystyki i typ (ale nie ruchy) każdego potwora to wiedza publiczna. Twoi przeciwnicy będą mogli zobaczyć, z czym walczą, aby wybrać najlepszą akcję. Dostępne są jednak również ukryte bonusy.

W szczególności po każdych dwóch bitwach otrzymasz jeden „bonusowy” punkt statystyczny za każdego potwora w twojej drużynie. Punkty przyznawane są wszystkim potworom, martwym lub żywym, zwycięzcy lub przegranemu. Możesz przypisać to do dowolnej z trzech wybranych statystyk. Nie możesz kłaść ich na jednym potworze; każdy potwór dostaje jeden za każdym razem. Punkty te są odporne na limit 100. Ponieważ będzie 100 rund bitewnych, oznacza to, że możesz zdobyć jedną statystykę do 149, jeśli przydzielisz jej wszystkie swoje bonusy. Ponownie przeciwnik zobaczy tylko twoje „podstawowe” statystyki, więc im dalej w turnieju jesteś, tym bardziej ich wiedza odbiega od prawdy.

Bitwa

Bitwa rozgrywa się pomiędzy trzyosobowymi drużynami, z jedną aktywną dla każdej drużyny na raz. Na początku zostanie wyświetlona drużyna przeciwnika i zostaniesz poproszony o wybranie potwora, który będzie Twoim pierwszym „aktywnym” graczem.

Następnie następują tury, wykonując następujące czynności:

  • Przełącznik: obowiązkowe przełączniki potworów mają miejsce (jeśli występują)
  • Wybierz akcję bitewną
  • Przełącznik: mają miejsce dowolne opcjonalne przełączniki potworów (wybrane jako akcja bitwy)
  • Sleep Check: szansa na przebudzenie ze snu
  • Atak 1: Jeśli to możliwe, szybszy potwór wykorzystuje wybrany ruch
  • Atak 2: Jeśli to możliwe, drugi potwór wykorzystuje wybrany ruch
  • Obrażenia od efektów: Zadaj obrażenia od oparzeń / trucizny żywym potworom

„Szybszy” oznacza potwora o większej prędkości. Jeśli obie statystyki prędkości są takie same, PRNG wybiera rzut monetą w każdej turze.

Pod koniec każdej tury, w której umiera Twój aktywny potwór, zostaniesz poproszony o wybranie nowego aktywnego. Możesz również zmienić aktywne potwory jako ruch na każdą turę (pod warunkiem, że masz więcej niż jednego żywego). Ponownie, jeśli zmienisz swój ruch, nie wykonasz ruchu bitewnego w tej turze.

Potwory nie są „przetwarzane”, gdy są nieaktywne. Oznacza to, że nie przyjmują obrażeń od poparzeń / trucizn, liczniki trucizn nie kumulują się, nie budzą się ze snu itp. Podczas przełączania nie są usuwane ani zmieniane żadne efekty . To nie jest inna gra z potworami. Jeśli wyłączysz się z podniesionym i spalonym atakiem, nadal będą tam, gdy wrócisz.

Obrażenia efektowe mają miejsce niezależnie od tego, czy zabijesz aktywnego przeciwnika, czy nie. W ten sposób członkowie obu drużyn mogą umrzeć w jednej turze.

Kiedy jednej drużynie zabraknie przydatnych potworów, przegrywają. Jeśli obie drużyny kończą się w tej samej turze, to remis. Jeśli bitwa trwa 1000 tur, to remis.

Formuła określająca uszkodzenie jest następująca:

floor((effAttack / effDefense) * movePower * typeMultiplier * moveBoost)

effAttacki effDefenseskutecznymi statystykami dla potworów. Skuteczny atak uzyskuje się poprzez dodanie Ataku i Ataku bonusowego, a następnie pomnożenie (o 0,8 lub 1,25), jeśli jakikolwiek efekt go zmieni. Pamiętaj, że te efekty mogą się nakładać.

Obrażenia mogą wynosić 0, gdy modyfikatorem typu jest 0 (Normalna <--> Wróżka) lub Moc ruchu wynosi 0 (Leczenie, Podpalenie itp.). W przeciwnym razie minimum jest egzekwowane na poziomie 1.

Zawody

Turnieje trwają 100 rund. W każdej rundzie drużyny są losowo tasowane i łączone ze sobą. Jeśli jest nieparzysta liczba drużyn, reszta otrzymuje pa (wyniki jako remis). Zwycięstwo w bitwie daje drużynie 2 punkty, remisy są warte 1 i nic nie tracą. Zespół z największą liczbą punktów na koniec wygrywa!

Jeśli drużyny są remisowane, odbędzie się turniej z drużynami remisowanymi na pierwszym miejscu w celu ustalenia kolejności remisów.

Protokół

Kontroler wyśle ​​programowi jedno z czterech poleceń. Pierwszy znak określa typ polecenia, w razie potrzeby następujące dane.

Twój program zaakceptuje polecenie jako argument i odpowie na STDOUT w ciągu jednej sekundy . Nie żyj słuchając STDIN, nie będzie go tam. Każde polecenie spowoduje pojawienie się nowego procesu.

Możesz zapisać dane / stan na dysku. Umieść dowolne pliki w podfolderze o tej samej nazwie co Twój zespół. Nie zapisuj więcej niż 32 kilobajtów danych, w przeciwnym razie zostaniesz zdyskwalifikowany. Dane będą się utrzymywać między rundami, ale będą usuwane między turniejami.

Polecenia

Dane zespołu

To jest wysyłane raz na początku turnieju, aby zarejestrować swoją drużynę. Twoja odpowiedź powinna być stała , nie różna dla każdego turnieju.

Pytanie:

T

Odpowiedź; reakcja; reagowanie; odzew; oddźwięk:

name|member0|member1|member2

nameto ciąg z nazwą twojego zespołu. Proszę używać tylko alfanumerycznych, dla ułatwienia parsowania. memberNto ciąg członkowski, podający szczegóły każdego potwora:

Ciąg członka:

name:typeid:attack:defense:speed:moveid0:moveid1:moveid2

Znów „imię” to ciąg znaków, tym razem z imieniem tego potwora. typeidjest tego typu. Identyfikatory typów są w kolejności pokazanej na powyższej tabeli, przy Normalnej = 0 i Trawie = 4.

Następne trzy pola to twoje podstawowe statystyki. Pamiętaj o limitach opisanych w sekcji statystyk powyżej.

Ostatnie trzy to ruchy twojego potwora. Identyfikatory pokazano na powyższym wykresie ruchów.

Przykładowa odpowiedź danych zespołu może wyglądać następująco:

DummyTeam|DummyA:0:50:60:70:0:1:2|DummyB:0:50:60:70:0:1:2|DummyC:0:50:60:70:0:1:2

Każdy zespół, który odeśle tu śmieci, źle sformatowane lub nielegalne dane, nie weźmie udziału, dopóki nie zostanie naprawiony.

Wybierz Aktywny

Jest to wysyłane na początku każdej bitwy, a gdy potwór umiera i musi zostać zmieniony.

Pytanie:

C#battleState

battleStatepokazuje stan bieżącej bitwy. Miejcie mnie tutaj, jest brzydka:

yourTeamState#theirTeamState

Gdzie XteamStatewygląda:

name:activeSlot|member0state|member1state|member2state

activeSlotpokazuje, który potwór jest aktualnie aktywny (0-2). Państwa członkowskie mają dwa smaki. Jeśli to twój zespół, daje dodatkowe informacje. Więc,

Twój członek

name:id:attack:defense:speed:hp:typeid:poisonedturns:moveCount0:moveCount1:moveCount2:bonusAttack:bonusDefense:bonusSpeed:effectid:effectid:effectid

Ich członek Xstate:

name:id:attack:defense:speed:hp:typeid:poisonedturns:effectid:effectid:effectid

idto po prostu identyfikator liczb całkowitych, którego możesz użyć do śledzenia potworów, jeśli nie lubisz ich używać name.

attack:defense:speedsą twoje podstawowe statystyki.

poisonedturns mówi ci, ile razy zostałeś otruty.

moveCountXinformuje o liczbie pozostałych zastosowań dla każdego ruchu. Jeśli 0, nie można go użyć. W przypadku nieograniczonej liczby ruchów będzie to ujemne.

bonus(stat) to ilość punktów bonusowych przypisanych do każdej statystyki.

effectidjest zmienną wielkością listą efektów, które zostały zastosowane do twojego potwora. Nie będzie nie być wleczone :na ciąg, czy istnieją aktywne efekty czy nie. Jeśli są ułożone efekty, pojawią się one jako wiele efektów na liście.

Identyfikatory efektów to:

0  NONE           (should not appear, internal use)
1  POISON
2  CONFUSION 
3  BURN 
4  SLEEP 
5  HEAL           (should not appear, internal use)
6  ATTACK_UP
7  ATTACK_DOWN
8  DEFENSE_UP
9  DEFENSE_DOWN
10 SPEED_DOWN

Odpowiedź; reakcja; reagowanie; odzew; oddźwięk:

memberSlot

Jedyną wymaganą odpowiedzią jest pojedyncza liczba 0,1,2, która mówi, który członek ma być aktywny. To musi być członek, który jest w stanie walczyć. Nie odeślij, 1jeśli członek 1 nie żyje.

Akcja bitwy

W każdej turze musisz zdecydować, co robić.

Pytanie:

A#battleState

battleStateTutaj jest dokładnie tak, jak to opisano powyżej.

Odpowiedź; reakcja; reagowanie; odzew; oddźwięk:

Aby użyć ruchu, odeślij miejsce, w którym się znajduje. Na przykład, jeśli przypisałem Punch do slotu 0, wysyłanie 0wykonuje Punch.

Aby przełączyć się na innego członka, wyślij slot członka plus dziesięć . Aby przejść do członka 2, wyślij 12.

Wszystko, czego nie ma w [0,1,2,10,11,12], jest uważane za nieprawidłowe i nie spowoduje podjęcia żadnych działań w tej turze.

Statystyki bonusowe

Po każdych dwóch bitwach otrzymujesz tajny punkt bonusowy dla każdego członka drużyny.

Pytanie:

B#yourTeamState

Stan twojego zespołu jest taki sam, jak pokazano powyżej, nie każ mi tego powtarzać.

Odpowiedź; reakcja; reagowanie; odzew; oddźwięk:

stat0:stat1:stat2

Twoja odpowiedź będzie reprezentować, jakie statystyki zwiększyć dla każdego członka zespołu. Atak wynosi 0, obrona wynosi 1, prędkość wynosi 2.

Aby zwiększyć prędkość członka, atak drugiego członka i obronę trzeciego członka, odpowiedziałbyś:

2:0:1

Kontroler

Kontroler można znaleźć na BitBucket: https: //Geobits@bitbucket.org/Geobits/codemon.git

Po prostu wrzuć wszystkie skompilowane pliki klas, zgłoszenia i players.conf do folderu i uruchom.

Nazywana jest główna klasa kontrolera Tournament. Zastosowanie to:

java Tournament [LOG_LEVEL]

Poziomy dziennika od 0-4 dają coraz więcej informacji. Poziom 0 prowadzi turniej po cichu i po prostu daje wyniki, a poziom 3 daje szczegółowe komentarze. Poziom 4 to wyjście debugowania.

Możesz dodać zgłoszenia do turnieju w players.confWystarczy dodać ciąg wiersza poleceń potrzebny do uruchomienia programu, po jednym w wierszu. Linie zaczynające się od #są komentarzami.

W swoim poście dołącz polecenie, które będę musiał dodać do mojego players.conf, oraz wszelkie kroki kompilacji (jeśli są wymagane).

Uwzględniono drużynę-manekina złożoną ze wszystkich członków Normalnych z trzema ruchami Normalnymi. Wybierają ruchy losowo i mają okropne statystyki. Baw się dobrze, pokonując ich.

Różne zasady

  • Nie możesz czytać ani pisać do żadnych zasobów zewnętrznych (z wyjątkiem własnego podfolderu, do 32 kB, jak wspomniano powyżej).

  • Twoja drużyna musi wejść do turnieju „w ciemno”. Oznacza to, że nie możesz analizować źródła innych osób, aby dowiedzieć się, co konkretna drużyna / potwór zrobi w danej sytuacji. Państwo może analizować ruchy przeciwnika / statystyk i śledzić w miarę trwania turnieju, ale nie sztywno tę informację.

  • Nie ingeruj w inne procesy / zgłoszenia. Bez przywoływania ich, używania refleksji, aby uzyskać dostęp do danych itp. Nie zadzieraj z moim komputerem. Po prostu nie próbuj tego. To zależy ode mnie. Osoby naruszające prawo mogą zostać wykluczone z przyszłego wejścia.

  • Uczestnicy są ograniczeni do maksymalnie dwóch zgłoszeń. Jeśli prześlesz więcej, zdobędę tylko dwa pierwsze przesłane. Jeśli chcesz je odwołać, usuń je.

  • Wpisy mogą nie istnieć wyłącznie w celu wspierania innych wpisów. Nie możesz też próbować pośrednio dyskwalifikować innych zawodników (na przykład, używając nazwy drużyny 27M dla graczy DQ, którzy próbują zapisać to na dysk). Każde zgłoszenie powinno być odtwarzane, aby wygrać na własną korzyść.

  • Twój program może odradzać maksymalnie jeden proces potomny na raz (całkowita liczba potomków, a nie bezpośredni). Zarówno główny, jak i dowolny proces potomny muszą zostać zakończone bezpośrednio po podaniu danych wyjściowych. Tak czy inaczej, upewnij się, że nie przekroczysz limitu czasu.

  • Turniej odbędzie się na moim komputerze z systemem Ubuntu z procesorem Intel i7 3770K.

Wyniki

To są wyniki obecnych graczy. Pomiędzy najlepszymi rywalami jest bardzo blisko, a ja myślę o zwiększeniu liczby rund do 500 (i dostosowaniu odstępu punktów bonusowych do dopasowania). Jakieś zastrzeżenia, komentarze?

------- Final Results -------

158     Happy3Campers
157     LittleKid
71      InsideYourHead
68      HardenedTrio
46      BitterRivals

Pełne wyniki play-by-play na Dysku Google


62
Chcę być najlepszy / Jak żaden kod nigdy nie był / Nie zawieszanie się jest moim testem / Debugowanie jest moją przyczyną! / Będę podróżował po sieci LAN / Skrypty daleko i szeroko / Próbuję ciężko zrozumieć / Dlaczego mój BIOS jest smażony! / Codémon, to ty i ja / Gra w golfa wszystko, co widzi oko / Codémon, jesteś moim najlepszym przyjacielem / Po zakończeniu programu! / Codémon, lang so true / Żadne awarie nas nie pociągną / Naucz mnie, a ja cię nauczę / Codémon, muszę je wszystkie pograć w golfa!
Kaz Wolfe

1
Zamiast zwiększać rundy do 500, byłoby miło, gdyby jedna runda składała się ze wszystkich, którzy walczą ze wszystkimi. Koniec byez nierówną liczbą zawodników i upewni się, że pary meczów są uczciwe i równomiernie rozłożone.
foobar

@foobar Chciałem tego uniknąć, ponieważ skaluje bitwy n^2zamiast n. Przy obecnych 7 zawodnikach i 100 rundach, to 2100 bitew (w porównaniu z obecnymi 300 i 1500 z 500 rundami). Jest coraz gorzej, gdy pojawia się więcej wpisów. Mógłbym zmniejszyć liczbę rund, ale waham się to zrobić ze względu na naturalną zmienność (w odniesieniu do statusów esp), a posiadanie wielokrotności 50 (dla punktów bonusowych) jest łatwiejsze.
Geobity

Czy to wyzwanie nie wymaga aktualizacji? :)
GholGoth21

@ GholGoth21 Tak, wierzę, że tak. Prawdopodobnie nie mogę się dzisiaj dostać, ale może jutro lub następnego dnia. Pinguj mnie na czacie, jeśli nie zostanie zaktualizowany do czwartku, jeśli chcesz.
Geobits

Odpowiedzi:


16

Zadowoleni 3 obozowicze - PHP

Grupa tchórzy, którzy lubią obalać przeciwników zaklętymi zaklęciami i patrzeć, jak gniją.

EDYCJA : Pan Lumpy został surowo ukarany i nie będzie już wypowiadał złych słów


PoręcznyPoręcznyGrass - atk:50 def:99 spd:81 Confuse Poison Heal

Jadowity bóbr bez ramienia, który lubi mylić ludzi z problematycznymi uściskami dłoni


FlippyFlippyWater - atk:50 def:99 spd:81 Confuse Burn Heal

Weteran na tablicy ogłoszeń, który ma słabość do głupich rozmów i wojen z ogniem.


OrzechowyOrzechowyFire - atk:50 def:99 spd:81 Burn Poison Heal

Broń masowego rażenia to jego ulubione cukierki.


GrudkowatyGrudkowatyPhp - lines:500 clarity:05 spd:01 Gather Guess Store

Dzięki prawie 2 cyfrom IQ i fenomenalnej pamięci Lumpy może odgadnąć ruchy wroga. Cóż, głównie.


Strategia

Strategia polega na jak najszybszym zatruciu, spaleniu i zmieszaniu przeciwników.
Sen nie był używany, ponieważ wydawał się mniej potężny niż 3 zaklęcia powyżej.
Na dłuższą metę zamieszanie jest śmiertelnie niebezpieczne, ponieważ zmniejsza ataki o 30% (zarówno zadawanie obrażeń, jak i rzucanie zaklęć), zapobiega uzdrawianiu się i źle szkodzi ciężkim trafieniom (potwór o 50 def / 100 ataków zada sobie 20 punktów obrażeń ).

Po dokładnym wklejeniu wroga moi obozowicze po prostu patrzą, jak skwierczy, gnije i uderza się na śmierć.

Wysoka obrona i leczenie służą do łagodzenia obrażeń zadawanych podczas agonii.

Podczas gdy moi 3 obozowicze walczą, magiczny jeleń Lumpy obserwuje wrogów każdym ruchem, a czasem udaje się je zidentyfikować. Informacje są przekazywane naszym myśliwcom, którzy dokładają wszelkich starań, aby z nich skorzystać.

Po obronie najważniejsza jest statystyka do zwiększenia.
Inicjatywa jest niezbędna do zastosowania leczenia przed kolejnym uderzeniem.

Atak w ogóle nie jest używany.

Czy czary są najlepszą bronią?

Zaklęcia takie jak trucizna, palenie i dezorientacja wymykają się ogólnej logice skały / papieru / nożyczek innych ataków.

Kiedy potwór zostanie dotknięty, nadal będzie tracił PŻ nawet po śmierci czarownika. To tak, jakby duch rzucającego wciąż go atakował.
Poza tym trucizna staje się szybko silniejsza niż w pełni wzmocniony, masywny atak (powyżej 50 punktów po 5 turach).

Oczekiwana długość życia zatrutego i spalonego potwora nie przekracza 8 tur, nawet przy 3 uzdrowieniach.

Jak wskazują boty Martina , bilans gry jest całkiem dobry.
Zasadniczo jest to inicjatywa, która zapewni równowagę między czystymi rzucającymi czary a czystymi atakującymi.

Kod

Wywołaj z php campers.php

To brzydki bałagan, ale szczerze mówiąc interfejs też nie pomaga.

Teraz, gdy pojawiła się odpowiednio agresywna konkurencja, zrealizowałem zgadywanie od dawna planowanych ruchów wroga.
Analiza ataków wymaga różnych dedukcji akrobacyjnych i trwałego przechowywania stanu ostatniej tury, co oznacza wojnę na pełną skalę z interfejsem kontrolera paranoicznego.
Nie jest to ani ładna rzecz, ani sześcionożny jackrabbit, ale wykonuje odpowiednią robotę.

<?php

// ============================================================================
// Game
// ============================================================================
class G {
    static $code_type = array ("Normal", "Psychic", "Fire", "Water", "Grass", "?", "self"); 
    static $code_move = array    ("Punch", "Heal", "Slow", "Pain", "Sleep", "Weaken", "Fireball", "Burn", "Sharpen", "Watergun", "Confuse", "Shield", "Vine", "Poison", "Sap", "?", "self", "pass");
    static $move_uses = array (1000,3,5,1000,3,5,1000,3,5,1000,3,5,1000,3,5,   2000,2000);
    static $move_type      = array (0,0,0,1,1,1,2,2,2,3,3,3,4,4,4, 5,5,5);
    static $move_dmg       = array (20,0,10,20,0,10,20,0,10,20,0,10,20,0,10,  20,10,0);
    static $move_forbidden = array (1,1,1,0,0,0,4,4,4,2,2,2,3,3,3);
    static $code_effect = array ("N", "Poison", "Confuse", "Burn", "Sleep", "H", "Sharpen", "Weaken", "Shield", "Sap", "Slow"); 
    static $decode_type, $decode_move, $decode_effect;
    static $damage_multiplier = array (
        array (2, 0, 1, 1, 1, 0),
        array (0, 2, 1, 1, 1, 0),
        array (1, 1,.5, 2,.5, 0),
        array (1, 1,.5,.5, 2, 0),
        array (1, 1, 2,.5,.5, 0),
        array (2, 2, 2, 2, 2,-1),
        array (9, 9, 9, 9, 9, 9, 1));
    static $atk_score = array ("Poison"=> 1002, "Confuse"=>1001, "Burn"=>1000);
    static $status_field = "atk:def:spd:hp:type:Pturns";
    static $all_moves, $strong_moves, $medium_moves, $effect_moves, $possible_moves;

    function init()
    {
        self::$status_field = explode (":", self::$status_field);
        foreach (array ("type", "move", "effect") as $table) self::${"decode_$table"} = array_flip (self::${"code_$table"});
        foreach (self::$code_move as $c=>$m)
        {
            if ($m == "?") break;
            self::$all_moves[] = new Move($m);
            if (self::$move_uses[$c] >  5) self::$strong_moves[] = $m;
            if (self::$move_uses[$c] == 5) self::$medium_moves[] = $m;
            if (self::$move_uses[$c] == 3) self::$effect_moves[] = $m;
            for ($type = 0 ; $type != 5 ; $type++) if ((self::$move_uses[$c] >  5) && (self::$move_forbidden[$c] != $type)) self::$possible_moves[$type][] = $m;
        }
    }

    function __construct ($name, $team)
    {
        $this->turn = 0;
        $this->name = $name;
        $this->team = $team;
        $this->results_pending = false;
    }

    function parse_team ($tpack, $own_team)
    {
        $pack = explode ("|", $tpack);
        list ($name,$active) = explode (":", array_shift($pack));
        if ($own_team)
        {
            $team = $this->team;
        }
        else
        {
            if (!isset($this->enemies[$name])) $this->enemies[$name] = new Team(array (new Monster (), new Monster (), new Monster ()));
            $team = $this->foes = $this->enemies[$name];
        }
        $team->active = $active;
        foreach ($pack as $i=>$mpack) $team->monster[$i]->parse_monster ($own_team, $mpack);
    }

    function choose_active ()
    {
        // detect start of round
        $team = $this->team;
        $foes = $this->foes;
        foreach ($team->monster as $i=>$m) if ($m->hp > 0) $candidate[$i] = $m;
        if (count ($candidate) == 3)
        {
            $this->results_pending = false;
            $this->round++;

            // reinitialize all monsters
            foreach (array($team, $foes) as $t)
            foreach ($t->monster as $m)
                $m->start_round();

            // guess initial opponent
            $opponent = $foes->initial_opponent();
        }
        else
        {
            $this->analyze_last_round();
            $opponent = $foes->active();
        }
        return $this->do_switch ($opponent);
    }

    function choose_attacker ($foe)
    {
        foreach ($this->team->monster as $i=>$m) if ($m->can_attack($foe)) $candidate[$i] = $m;
        if (isset($candidate))
        {
            uasort ($candidate, function ($a,$b) use ($foe) { return ($a->atk_score != $b->atk_score) ? $b->atk_score - $a->atk_score : $b->life_expectancy($foe) - $a->life_expectancy($foe); });
            return key($candidate);
        }
        return -1;
    }

    function do_switch ($foe)
    {
        $replacement = $this->choose_attacker ($foe);
        if ($replacement < 0)
        {
            $candidate =  $this->team->monster;
            uasort ($candidate, function ($a,$b) use ($foe) { return $b->life_expectancy($foe) - $a->life_expectancy($foe); });
            $replacement = key($candidate);
        }

        $this->old_own = $this->team->monster[$replacement];
        $this->old_own->attack = "pass";
        return $replacement;
    }

    function choose_action ()
    {
        $this->analyze_last_round();
        $own = $this->team->active();
        $foe = $this->foes->active();
        $this->old_own = $own;

        if ($own->hp <= $own->max_damage($foe) && $own->can_do ("Heal")) return $own->execute("Heal");
        if ($attack = $own->can_attack($foe)) return $own->execute($attack);
        if ($own->hp <= 50 && $own->can_do ("Heal")) return $own->execute("Heal");

        return 10 + $this->do_switch ($foe);    
    }

    function choose_bonus()
    {
        foreach ($this->team->monster as $m)
        {
            if ($m->spd_b == 0) { $m->spd_b++; $res[] = 2; }
            else                { $m->def_b++; $res[] = 1; }
        }
        return implode (":", $res);
    }

    function parse ($parts)
    {
        self::parse_team ($parts[1], true);
        self::parse_team ($parts[2], false);    
    }

    function analyze_last_round()
    {
        if ($this->results_pending)
        {
            $this->results_pending = false;

            $foes = $this->foes;
            $foe = null;
            foreach ($foes->monster as $m) if ($m->hp != $m->old->hp) $foe = $m;
            if ($foe === null) $foe = $foes->monster[$foes->active];

            $this->old_own->guess_attack($foe);
        }
    }

    function process ($line)
    {
        $parts = explode ("#", $line);
        switch ($parts[0])
        {
        case "T": // register for tournament
            echo "$this->name|$this->team";
            break;
        case "C": // designate active monster
            $this->parse ($parts);
            echo $this->choose_active();
            break;
        case "A": // choose round action
            $this->parse ($parts);
            echo $this->choose_action();

            // save current state
            foreach (array($this->team, $this->foes) as $t)
            foreach ($t->monster as $m)
            {
                unset ($m->old);
                $m->old = clone ($m);
            }
            $this->results_pending = true;
            break;
        case "B": // distribute stat bonus
            echo $this->choose_bonus();
            break;
        }

    }
}
G::init();

// ============================================================================
// Move
// ============================================================================
class Move {
    function __construct ($move)
    {
        $this->register($move);
    }

    function register ($move)
    {
        $this->type = G::$decode_move[$move];
        $this->reinit();
    }

    function reinit()
    {
        $this->uses = G::$move_uses[$this->type];
    }

    function __tostring() { return G::$code_move[$this->type]."($this->uses)"; }
}

// ============================================================================
// Monster
// ============================================================================
class Monster { 
    function __construct ($name="?", $type="?", $atk=100, $def=100, $spd=100, $m0="?", $m1="?", $m2="?")
    {
        $this->name = $name;
        $this->type = G::$decode_type[$type];
        $this->atk  = $atk;
        $this->def  = $def;
        $this->spd  = $spd;
        $this->hp   = 100;
        $this->move = array (new Move($m0), new Move($m1), new Move($m2));
        $this->atk_b = 0;
        $this->def_b = 0;
        $this->spd_b = 0;
        foreach (G::$code_effect as $e) $this->$e = 0;
    }

    function __tostring ()
    {
        return implode (":", array (
            $this->name,
            $this->type,
            $this->atk,
            $this->def,
            $this->spd,
            $this->move[0]->type,
            $this->move[1]->type,
            $this->move[2]->type));
    }

    function start_round()
    {
        foreach ($this->move as $m) $m->reinit();
    }

    function parse_monster ($own_team, $spack)
    {
        $pack = explode (":", $spack);
        $name = array_shift ($pack); // get name
        array_shift ($pack); // skip id
        if ($this->name == "?") $this->name = $name; // get paranoid
        else if ($this->name != $name) die ("expected $this->name, got $name");

        // store updated values
        foreach (G::$status_field as $var) $this->$var = array_shift ($pack);
        if ($own_team)
        {
            foreach ($this->move as $m) $m->new_count = array_shift($pack);
            $pack = array_slice ($pack, 3); // these are maintained internally
        }
        $var = array();
        foreach ($pack as $e) @$var[G::$code_effect[$e]]++; 
        foreach (G::$code_effect as $e) $this->$e = @$var[$e]+0;
    }

    function damage_recieved ($attack, $foe=null)
    {
        if ($attack == "self") $foe = $this;
        $a = G::$decode_move[$attack];
        $type = G::$move_type[$a];
        $dmg = g::$move_dmg[$a];

        if ($dmg == 0) return 0;

        $atk = ($foe ->atk+$foe ->atk_b) * pow (.8, ($foe ->Weaken - $foe ->Sharpen));
        $def = ($this->def+$this->def_b) * pow (.8, ($this->Sap    - $this->Shield ));

        $boost = ($foe->type == $type) ? 1.2 : 1;
        return max (floor ($dmg * $atk / $def * $boost * G::$damage_multiplier[$this->type][$type]), 1);
    }

    function guess_attack_from_effect ($attacks)
    {
        foreach ($attacks as $status) if ($this->$status != $this->old->$status) return $status;
        return "?";
    }

    function guess_attack_from_damage ($foe, $damages)
    {
        $select = array();
        foreach (G::$possible_moves[$foe->type] as $attack)
        {
            $dmg = $this->damage_recieved ($attack, $foe);
            foreach ($damages as $damage) if ($damage != 0 && abs ($dmg/$damage-1) < 0.1) $select[$attack] = 1;
        }
        $res = array();
        foreach ($select as $a=>$x) $res[] = $a;
        return $res;
    }

    function guess_attack ($foe)
    {
        $attempt = G::$decode_move[$this->old->attack];
        $success = ($this->old->attack == "pass");
        foreach ($this->move as $m)
        {
            if ($m->type == $attempt)
            {
                if ($m->new_count == $m->uses-1)
                {
                    $m->uses--;
                    $success = true;
                }
                break;
            }
        }

        $possible = array();
        $attack = $this->guess_attack_from_effect (array("Burn", "Confuse", "Poison", "Sleep", "Slow", "Weaken", "Sap"));
        if ($attack == "?") $attack = $foe->guess_attack_from_effect (array("Sharpen", "Shield"));
        if ($attack == "?")
        {
            $foe_damage = $this->old->hp - $this->hp - (10 * $this->Burn + 5 * $this->Pturns*$this->Poison);
            if ($this->old->attack == "Heal" && $success) $foe_damage += 50;
            $possible_dmg[] = $foe_damage;
            //;!;if ($this->Confuse) $possible_dmg[] = $foe_damage + $this->damage_recieved ("self");
            $possible = $this->guess_attack_from_damage ($foe, $possible_dmg);
            if (count ($possible) == 1) $attack = $possible[0];
        }
        if ($attack == "?")
        {
            $own_damage = $foe->old->hp - $foe->hp 
                        - (10 * $foe->Burn + 5 * $foe->Pturns*$foe->Poison)
                        + $foe->damage_recieved ($this->attack);
            if (abs ($own_damage/50+1) < 0.1) $attack = "Heal";
        }
        if ($attack != "?")
        {
            $type = G::$decode_move[$attack];
            if ($attack != "?")
            {
                foreach ($foe->move as $m) if ($m->type == $type) goto found_old;
                foreach ($foe->move as $m) if ($m->type == 15) { $m->register($attack); goto found_new; }
            }
            found_new:
            found_old:
        }
    }

    function max_damage($foe)
    {
        $dmg = 0;
        foreach ($foe->move as $m) $dmg = max ($dmg, $this->damage_recieved (G::$code_move[$m->type], $foe));
        return $dmg;
    }

    function expected_damage ($foe)
    {
        return $this->max_damage($foe) + 10 * $this->Burn + 5 * ($this->Pturns+1);
    }

    function life_expectancy ($foe)
    {
        $hp = $this->hp;
        $poison = $this->Pturns;
        $heal = $this->can_do ("Heal");
        $dmg = $this->max_damage($foe);
        for ($turn = 0 ; $hp > 0 && $turn < 10; $turn++)
        {
            $hp -= 10 * $this->Burn + 5 * $poison;
            if ($poison > 0) $poison++;
            $hp -= $dmg;
            if ($hp <= 0 && $heal > 0) { $hp+=50; $heal--; }
        }
        return 100 * $turn + $this->hp;
    }

    function can_attack ($foe)
    {
        $attack = false;
        if ($this->hp > 0)
        {
            if      (!$foe->Poison  && $this->can_do ("Poison" )) $attack = "Poison";
            else if (!$foe->Confuse && $this->can_do ("Confuse")) $attack = "Confuse";
            else if (!$foe->Burn    && $this->can_do ("Burn"   )) $attack = "Burn";
        }
        $this->atk_score = ($attack === false) ? 0 : G::$atk_score[$attack];
        return $attack;
    }

    function can_do($move)
    {
        $type = G::$decode_move[$move];
        foreach ($this->move as $m) if ($m->type == $type && $m->uses > 0) return $m->uses;
        return false;
    }

    function execute($move)
    {
        $type = G::$decode_move[$move];
        foreach ($this->move as $i=>$m) if ($m->type == $type) 
        { 
            if ($m->uses > 0)
            {
//;!;               $m->uses--;
                $this->attack = $move;
            }
            else $this->attack = "pass";
            return $i; 
        }
        die ("$this asked to perform $move, available ".implode(",", $this->move));
    }
}

// ============================================================================
// Team
// ============================================================================
class Team {
    function __construct ($members)
    {
        $this->monster = $members;
    }

    function __tostring()
    {
        return implode ("|", $this->monster);
    }

    function active ()
    {
        return $this->monster[$this->active];
    }

    function initial_opponent()
    {
        return $this->monster[0];
    }
}

// ============================================================================
// main
// ============================================================================
$input = $argv[1];

$team_name = "H3C";
$mem_file = "$team_name/memory.txt";
$trc_file = "$team_name/trace.txt";
if (!file_exists($team_name)) mkdir($team_name, 0777, true) or die ("could not create storage directory '$team_name'");
if ($input == "T") array_map('unlink', glob("$team_name/*.txt"));

if (file_exists($mem_file)) $game = unserialize (file_get_contents ($mem_file));
else
{
    $team = new Team (
        array (
            new Monster ("Handy" , "Grass" , 50, 99, 81, "Confuse", "Poison", "Heal"),
            new Monster ("Nutty" , "Fire"  , 50, 99, 81, "Burn"   , "Poison", "Heal"),
            new Monster ("Flippy", "Water" , 50, 99, 81, "Confuse" , "Burn" , "Heal")));
    $game = new G($team_name,$team);
}

$game->process ($input);
file_put_contents ($mem_file, serialize($game));

Wyniki

LittleKid jest nadal groźny, ale moje trio pokonało swoich jadowitych dziwaków z całkiem sporą przewagą.

Boty Martina skazane są na brak inicjatywy, a zwiększenie ich prędkości wymagałoby zmniejszenia ataku, co osłabiłoby ich przewagę pod względem zdolności do zadawania obrażeń.

Nowi pretendenci z JavaScript JavaScript są na równi z zespołem w pojedynku, ale gorzej radzą sobie z innymi konkurentami. W rzeczywistości pomagają obniżyć wynik LittleKid :).

Wygląda więc na to, że moi milutcy przyjaciele pozostają królami wzgórza - na razie ...

170             H3C
158             Nodemon
145             LittleKid
55              InsideYourHead
42              HardenedTrio
30              BitterRivals

Nie mam też PHP na swoim. Spodziewałbym się jednak, że ocierasz podłogę Metapodami, ponieważ wybierają powolny ruch i zostają zatrute na śmierć.
Sp3000,

... * i * spalone na ostro: D. Taki jest problem skomplikowanych zasad: bardzo prawdopodobne jest, że pojawi się dominująca strategia. Ponieważ nie ma ochrony przed tymi zaklęciami, mogą to być grubas i mały chłopiec codémon.

Porównałbym to bardziej do nieprzechodnich gier, takich jak nożyce, papier, kamień - ponieważ efekty trwają chwilę, zespół z pełnym atakiem powinien być w stanie cię pokonać :)
Sp3000

2
Tak, właśnie to sobie wyobrażałem, gdy tylko nie znalazłem lekarstwa na Poison and Burn. Dziękuję za urzeczywistnienie mojego marzenia.
justhalf

1
To pan Lumpy wyraża swoje zdziwienie, gdy wykrywa więcej niż 3 różne ataki od tego samego przeciwnika :). Mam naprawioną wersję prawie ukończoną, ale teraz jestem w trakcie jakiejś innej rzeczy, więc poprawka zostanie opublikowana za jakiś dzień.

7

HardenedTrio, Python 3

Ponieważ Geobits był wystarczająco miły, aby dać nam dwa zgłoszenia, pomyślałem, że prześlę coś głupiego na pierwszy: P

Drużyną jest trzech Codemon (Metapod1, Metapod2, Metapod3) z tymi samymi statystykami i ruchami:

  • 80 ataku, 100 obrony, 50 prędkości
  • Uderzenie, Uzdrowienie, Wzmocnienie Tarczy

Wszystkie punkty bonusowe są również przypisane do obrony.


from collections import namedtuple
import sys

BattleState = namedtuple("BattleState", ["us", "them"])
TeamState = namedtuple("TeamState", ["name", "active", "members"])
MemberState = namedtuple("MemberState", ["name", "id", "attack", "defense", "speed", "hp",
                                         "typeid", "poisonedturns", "otherstats"])

def parse_battle_state(state):
    return BattleState(*map(parse_team_state, state.split("#")))

def parse_team_state(state):
    na, *members = state.split("|")
    name, active = na.split(":")
    return TeamState(name, int(active), list(map(parse_member_state, members)))

def parse_member_state(state):
    name, id_, attack, defense, speed, hp, typeid, poisonedturns, *rest = state.split(":")
    return MemberState(name, int(id_), float(attack), float(defense), float(speed),
                       float(hp), int(typeid), int(poisonedturns), rest)

command = sys.argv[1].strip()

if command.startswith("T"):
    print("HardenedTrio|Metapod1:0:80:100:50:0:1:11|"
          "Metapod2:0:80:100:50:0:1:11|Metapod3:0:80:100:50:0:1:11")

elif command.startswith("C"):
    battle_state = parse_battle_state(command[2:])

    for i, codemon in enumerate(battle_state.us.members):
        if codemon.hp > 0:
            print(i)
            break

elif command.startswith("A"):
    battle_state = parse_battle_state(command[2:])
    current_codemon = battle_state.us.members[battle_state.us.active]

    if current_codemon.hp < 50 and int(current_codemon.otherstats[1]) > 0:
        print(1) # Heal up if low

    elif int(current_codemon.otherstats[2]) > 0:
        print(2) # Harden!

    else:
        print(0) # Punch!

elif command.startswith("B"):
    print("1:1:1")

Biegnij z

py -3 <filename>

(lub z python/ python3zamiast w pyzależności od instalacji)



1
@FryAmTheEggman Metapod, HARDEN!
Sp3000,

Próbuję odczytać twój kod, ale się pomyliłem int(current_codemon.otherstats[1])>0. To się zgadza, jeśli ma efekt statusu? I używa hartowania tylko wtedy, gdy ma dwa efekty statusu?
Mooing Duck

@MooingDuck Dla twojego Codemona masz moveCounts przed effectids, więc sprawdza, czy nadal może używać Harden. Byłem leniwy w przetwarzaniu, dlatego jest tam skupiony.
Sp3000,

@ Sp3000: Oh! Dobrze! HAHAHA!
Kaczka Mooing

6

W twojej głowie, Ruby

  • Brian : Wróżka, Atak: 100, Obrona: 50, Prędkość: 80, Ból, Kula ognia, Pistolet wodny
  • Elemon1 : Wróżki , Atak: 100, Obrona: 50, Prędkość: 80, Kula ognia, Pistolet wodny, Winorośl
  • Elemon2 : Wróżki , Atak: 100, Obrona: 50, Prędkość: 80, Kula ognia, Pistolet wodny, Winorośl
TEAM_SPEC = "InsideYourHead"+
            "|Brian:1:100:50:80:3:6:9"+
            "|Elemon1:1:100:50:80:6:9:12"+
            "|Elemon2:1:100:50:80:6:9:12"

def parse_battle_state request
    request.map do |team_state|
        state = {}
        parts = team_state.split '|'
        state[:active] = parts.shift.split(':')[1].to_i
        state[:monsters] = parts.map do |monster_state|
            monster = {}
            parts = monster_state.split(':')
            monster[:name] = parts[0]
            monster[:hp] = parts[5].to_i
            monster[:type] = parts[6].to_i
            monster
        end
        state
    end
end

request = ARGV[0].split '#'
case request.shift
when 'T'
    puts TEAM_SPEC
when 'C'
    battle_state = parse_battle_state request
    my_state = battle_state[0]
    puts my_state[:monsters].find_index {|monster| monster[:hp] > 0}
when 'A'
    battle_state = parse_battle_state request
    my_state, their_state = *battle_state
    my_monster = my_state[:monsters][my_state[:active]]
    their_monster = their_state[:monsters][their_state[:active]]
    puts [1,0,1,2,0][their_monster[:type]]
when 'B'
    puts '0:0:0'
end

Biegnij z

ruby InsideYourHead.rb

Nie działa to zbyt dobrze przeciwko botowi Manu, ale pokonuje pozostałe trzy. Nazwy drużyn i potworów są dość losowe ... Mogę je zmienić, jeśli wymyślę coś lepszego

Strategia jest dość prosta: atak! Wszystkie trzy potwory mają tylko czyste ruchy ataku i wybierają swój ruch na podstawie typu potwora przeciwnika.

Mogę eksperymentować z rzucaniem Uzdrowienia później.


1
Hehe, to staje się bardziej interesujące. Wiedziałem, że mogę na ciebie liczyć, Martin :)

6

LittleKid, Java

Małe dziecko znalazło 3 identyczne kodemony i je przeszkoliło. Są bardzo denerwujące z ich leczeniem + atakami trucizny. Używanie tylko kodonów normalnego typu eliminuje potrzebę parowania ich z konkretnymi wrogami, ponieważ trucizna działa dobrze na wszystkie typy.

public class LittleKid {

    public static void main(String[] args) {
        if(args.length < 1){
            System.out.println("Geobits says you can't do this.");
            System.exit(0);
        }

        String[] sections = args[0].split("#");
        String me, them, out = "";
        switch(sections[0]){
            case "T":
                out = "LittleKid";
                out += "|Poisoner:0:80:100:50:0:1:13";
                out += "|Poisoner:0:80:100:50:0:1:13";
                out += "|Poisoner:0:80:100:50:0:1:13";
                break;
            case "B":
                out = "1:1:1";
                break;
            case "C":
                me = sections[1];
                them = sections[2];
                int pick = 0;

                if(!isAlive(me, pick)){
                    for(int i=0;i<3;i++){
                        if(isAlive(me,i))
                            pick = i;
                    }
                }

                out = String.valueOf(pick);
                break;
            case "A":
                me = sections[1];
                them = sections[2];
                int active = getActive(me);
                int enemyActive = getActive(them);
                if (getField(me, HP, active) < 50 && getField(me, MOVE1, active) != 0) {
                    out = "1";
                } else if (getEffectCount(them, POISON, enemyActive, false) < 1 && getField(me, MOVE2, active) != 0) {
                    out = "2";
                } else {
                    out = "0";
                }
                break;
            default:
                out = "Invalid query from controller.";             
        }
        System.out.println(out);
    }

    static boolean isAlive(String teamState, int who){
        return getField(teamState, HP, who) > 0;
    }

    static int getActive(String teamState){
        return Integer.parseInt(teamState.split("\\|")[0].split(":")[1]);
    }

    static int getField(String teamState, int field, int who){
        String[] fields = teamState.split("\\|")[who+1].split(":");
        return Integer.parseInt(fields[field]);
    }

    static int getEffectCount(String teamState, int effect, int who, boolean mine){
            String[] fields = teamState.split("\\|")[who+1].split(":");
            int count = 0;
            for(int i=mine?14:8;i<fields.length;i++){
                if(Integer.parseInt(fields[i]) == effect)
                    count++;
            }
            return count;
    }

    final static int ID =       1; 
    final static int ATTACK =   2; 
    final static int DEFENSE =  3; 
    final static int SPEED =    4; 
    final static int HP =       5; 
    final static int TYPE =     6;
    final static int MOVE0 =    8; 
    final static int MOVE1 =    9; 
    final static int MOVE2 =    10;

    final static int POISON =           1;
}

5
Geobits mówi, że nie możesz tego zrobić ”: D
Geobits

Wygląda na to, że trucizna jest prawdziwą bombą typu A w tej grze :)

5

Nodémon - JavaScript

Ponieważ status wydaje się być dominującą strategią, drużyna ta koncentruje się na szybkości, aby najpierw zdobyć statusy takie jak trucizna i zamieszanie na przeciwnikach, a następnie zwlekać z leczeniem i / lub snem, podczas gdy przeciwnik marnuje się.

Nie mam zainstalowanego PHP, więc nie jest to testowane przeciwko Obozowiczom, ale wydaje się, że jest to porządny konkurent dla LittleKid w moich testach (i dziesiątkuje Bitter Rivals).

/*jshint node:true*/
'use strict';

var fs = require('fs');

var dataFile = 'Nodemon/data.json';
function getData(callback) {
  fs.readFile(dataFile, 'utf8', function(err, contents) {
    var data = {round: 0};

    if(!err) {
      data = JSON.parse(contents);
    }

    callback(data);
  });
}

function saveData(data, callback) {
  fs.mkdir('Nodemon', function() {    
    fs.writeFile(dataFile, JSON.stringify(data), callback);
  });
}

var effect = {
  poison: '1',
  confusion: '2',
  burn: '3',
  sleep: '4',
  heal: '5',
  attackUp: '6',
  attackDown: '7',
  defenseUp: '8',
  defenseDown: '9',
  speedDown: '10'
};

function parseMemberCommon(args) {
  return {
    name: args[0],
    id: args[1],
    baseAttack: +args[2],
    baseDefense: +args[3],
    baseSpeed: +args[4],
    hp: +args[5],
    typeId: args[6],
    poisonedTurns: +args[7],
    effects: args.slice(8)
  };
}

function parseOwnMember(arg) {
  var args = arg.split(':');

  var ownArgs = args.splice(8, 6);

  var member = parseMemberCommon(args);

  member.moveCount = [
    +ownArgs[0],
    +ownArgs[1],
    +ownArgs[2]
  ];

  member.bonusAttack = +ownArgs[3];
  member.bonusDefense = +ownArgs[3];
  member.bonusSpeed = +ownArgs[3];

  return member;
}

function parseOpponentMember(arg) {
  return parseMemberCommon(arg.split(':'));
}

function parseTeamStateCommon(arg, memberParse) {
  var args = arg.split(':');
  var state = {
    name: args[0],
    members: []
  };
  args = arg.substring(state.name.length + 1).split('|');
  var activeSlot = args[0];
  for(var index = 1; index < args.length; index++) {
    state.members.push(memberParse(args[index]));
  }
  state.activeMember = state.members[activeSlot];
  return state;
}

function parseOwnState(arg) {
  return parseTeamStateCommon(arg, parseOwnMember);
}

function parseOpponentState(arg) {
  return parseTeamStateCommon(arg, parseOpponentMember);
}

function parseBattleState(arg) {
  var args = arg.split('#');
  return {
    own: parseOwnState(args[0]),
    opponent: parseOpponentState(args[1])
  };
}

function teamData() {

  saveData({round:0}, function() {
    console.log('Nodemon|' + 
      'Charasaur:0:50:80:100:10:13:1|' +
      'Bulbtortle:4:50:80:100:10:13:1|' +
      'Squirtmander:1:50:80:100:10:13:4');
  });
}

function getActiveIndex(battleState) {
  for(var index = 0; index < battleState.own.members.length; index++) {
    var member = battleState.own.members[index];
    if(member.hp > 0) {
      return index;
    }
  }
}

function chooseActive(arg) {
  var battleState = parseBattleState(arg);

  getData(function(data) {
    var allFull = true;
    for(var index = 0; index < battleState.opponent.members.length; index++) {
      var member = battleState.opponent.members[index];
      if(!data.maxSpeed || member.baseSpeed > data.maxSpeed) {
        data.maxSpeed = member.baseSpeed;
      }
      if(member.hp < 100) {
        allFull = false;
      }
    }

    if(allFull) {
      data.round++;
    }

    saveData(data, function() {
      console.log(getActiveIndex(battleState));
    });    
  });
}

function useMove(moves, battleState) {
  var fighter = battleState.own.activeMember;

  for(var moveIndex = 0; moveIndex < moves.length; moveIndex++) {
    var move = moves[moveIndex];
    if(fighter.moveCount[move]) {
      return move;
    }

    for(var memberIndex = 0; memberIndex < battleState.own.members.length; memberIndex++) {
      var member = battleState.own.members[memberIndex];

      if(member.hp > 0 && member.moveCount[move] > 0) {
        return 10 + memberIndex;
      }
    }
  }

  return -1;  //do nothing
}

function battleAction(arg) {
  var battleState = parseBattleState(arg);

  var fighter = battleState.own.activeMember;
  var opponent = battleState.opponent.activeMember;

  var attemptedMoves = [];

  if(opponent.effects.indexOf(effect.poison) === -1) {
    attemptedMoves.push(1);
  }

  if(opponent.effects.indexOf(effect.confusion) === -1) {
    attemptedMoves.push(0);
  }

  if(fighter.name === 'Squirtmander') {
    //sleep
    if(opponent.effects.indexOf(effect.sleep) === -1) {
      attemptedMoves.push(2);
    }
  }
  else {
    //heal
    if(fighter.hp <= 60) {
      attemptedMoves.push(2);
    }
  }

  console.log(useMove(attemptedMoves, battleState));
}

function bonusStats(arg) {
  var teamState = parseOwnState(arg);

  getData(function(data) {
    var result = '1:';

    if(data.round % 4 === 0) {
      result += '1:';
    }
    else {
      result += '2:';
    }
    if(teamState.members[2].baseSpeed + teamState.members[2].bonusSpeed > data.maxSpeed + (data.round / 2)) {
      result += '1';
    }
    else {
      result += '2';
    }
    console.log(result);
  });
}

var actions = {
  'T': teamData,
  'C': chooseActive,
  'A': battleAction,
  'B': bonusStats
};

var arg = process.argv[2];
actions[arg[0]](arg.substring(2));

Biegnij z

node nodemon

PS Przepraszam nodemona .


To eskaluje do globalnej wojny skryptowej po stronie serwera: D

4

Bitter Rivals - Java

Zespół Grass / Fire / Water, który lubi to zmienić.

Greenozaur

Ma przynajmniej neutralny zasięg dla każdego. Duża prędkość, aby nadrobić brak obrony.

Type: Grass
Attack:   80     Vine
Defense:  50     Punch
Speed:   100     Pain

Wyszukiwarka

Stara się zasiać wrogów niskim atakiem. Potem pali się i kula ognia.

Type: Fire
Attack:  100     Fireball
Defense:  50     Burn
Speed:    80     Sap

Osłona przed falą uderzeniową

Używa tarczy, aby wzmocnić i tak już wysoką obronę. Leczy w razie potrzeby.

Type: Water
Attack:   80     Watergun
Defense: 100     Shield
Speed:    50     Heal

Kod

Jest to również zawarte w kontrolerze. To jest konkurencyjny zespół, w przeciwieństwie DummyTeam. Wymagane polecenie players.confto:

java BitterRivals

public class BitterRivals {

    public static void main(String[] args) {
        if(args.length < 1){
            System.out.println("You're not doing this right. Read the spec and try again.");
            System.exit(0);
        }

        String[] sections = args[0].split("#");
        String me, them, out = "";
        switch(sections[0]){
            case "T":
                out = "BitterRivals";
                out += "|Greenosaur:4:80:50:100:12:0:3";
                out += "|Searizard:2:100:50:80:6:7:14";
                out += "|Blastshield:3:80:100:50:9:11:1";
                break;
            case "B":
                out = "2:0:1";
                break;
            case "C":
                me = sections[1];
                them = sections[2];

                int pick = 0;
                switch(getField(them, TYPE, getActive(them))){
                    case 0:
                    case 1:
                    case 3:
                        pick = 0;
                        break;
                    case 2:
                        pick = 2;
                        break;
                    case 4:
                        pick = 1;
                        break;
                }

                if(!isAlive(me, pick)){
                    for(int i=0;i<3;i++){
                        if(isAlive(me,i))
                            pick = i;
                    }
                }

                out = pick + "";
                break;
            case "A":
                me = sections[1];
                them = sections[2];
                int active = getActive(me);
                int oType = getField(them, TYPE, getActive(them));
                switch(active){
                    case 0:         // Greenosaur
                        switch(oType){
                            case 0:
                            case 4:
                                out = "1";
                                break;
                            case 1:
                                out = "2";
                                break;
                            case 3:
                                out = "0";
                                break;
                            case 2:
                                if(isAlive(me, 2)){
                                    out = "12";
                                } else if(isAlive(me, 1)){
                                    out = "11";
                                } else {
                                    out = "1";
                                }
                                break;
                        }
                        break;
                    case 1:         // Searizard
                        if(oType == 3){
                            if(isAlive(me, 0)){
                                out = "10";
                                break;
                            } else if(isAlive(me, 2)){
                                out = "12";
                                break;
                            }
                            if(getEffectCount(them, BURN, getActive(them), false) < 1 && getField(me, MOVE1, active) > 0){
                                out = "1";
                            } else if(getField(me, MOVE2, active) > 0){
                                out = "2";
                            } else {
                                out = "3";
                            }                           
                        } else {
                            if(getField(them, ATTACK, getActive(them)) < 80){
                                if(getEffectCount(them, DEFENSE_DOWN, getActive(them), false) < 1 && getField(me, MOVE2, active) > 0){
                                    out = "2";
                                    break;
                                } else if(getEffectCount(them, BURN, getActive(them), false) < 1 && getField(me, MOVE1, active) > 0){
                                    out = "1";
                                    break;
                                }
                            }
                            out = "0";
                        }
                        break;
                    case 2:         // Blastshield
                        if(oType == 4){
                            if(isAlive(me, 1)){
                                out = "11";
                                break;
                            } else if(isAlive(me, 0)){
                                out = "10";
                                break;
                            }
                        }
                        if(getField(me, HP, active) < 50 && getField(me, MOVE2, active) > 0){
                            out = "2";
                        } else if(getEffectCount(me, DEFENSE_UP, active, true) < 3 && getField(me, MOVE1, active) > 0){
                            out = "1";
                        } else {
                            out = "0";
                        }
                        break;
                }
                break;
            default:
                out = "Invalid query from controller.";             
        }
        System.out.println(out);
    }

    static boolean isAlive(String teamState, int who){
        return getField(teamState, HP, who) > 0;
    }

    static int getActive(String teamState){
        return Integer.parseInt(teamState.split("\\|")[0].split(":")[1]);
    }

    static int getField(String teamState, int field, int who){
        String[] fields = teamState.split("\\|")[who+1].split(":");
        return Integer.parseInt(fields[field]);
    }

    static int getEffectCount(String teamState, int effect, int who, boolean mine){
            String[] fields = teamState.split("\\|")[who+1].split(":");
            int count = 0;
            for(int i=mine?14:8;i<fields.length;i++){
                if(Integer.parseInt(fields[i]) == effect)
                    count++;
            }
            return count;
    }

    final static int ID =       1; 
    final static int ATTACK =   2; 
    final static int DEFENSE =  3; 
    final static int SPEED =    4; 
    final static int HP =       5; 
    final static int TYPE =     6; 
    final static int PTURNS =   7; 
    final static int MOVE0 =    8; 
    final static int MOVE1 =    9; 
    final static int MOVE2 =    10; 
    final static int HA =       11; 
    final static int HD =       12; 
    final static int HS =       13; 

    final static int POISON =           1;
    final static int CONFUSION =        2;
    final static int BURN =             3;
    final static int SLEEP =            4;
    final static int ATTACK_UP =        6;
    final static int ATTACK_DOWN =      7;
    final static int DEFENSE_UP =       8;
    final static int DEFENSE_DOWN =     9;
    final static int SPEED_DOWN =       10;
}

4

Błąd 310: Zbyt wiele przekierowań - C ++

Dobrze wyszkolony i zorganizowany zespół do walki z spustoszeniami trucizny

Przez trzy tygodnie prawie nie trenowałem swoich codémons. Utworzyłem kilka zespołów. I wreszcie jestem gotowy na to wyzwanie. Aby odpowiedzieć na wszystkich moich przeciwników-trucicieli, stworzyłem drużynę z bardzo różnymi kodemonami, z których każdy ma określoną rolę.


Antidotum(wizerunek)

Type : Normal - atk:50 def:100 spd:80 - Poison/Burn/Heal

Antidotum uwielbia truciznę. Tak bardzo, że nie mogę go powstrzymać przed rzuceniem się na zatrucie.


Zen(wizerunek)

Type : Fire - atk:100 def:80 spd:50 - Poison/Vine/Heal

Zen to bardzo zaskakujący kodem, który uwzględnia wszystkie efekty. Woli patrzeć, jak jego wrogowie walczą i wyczerpują się w milczeniu.


Triforce(wizerunek)

Type : Psychic - atk:88 def:60 spd:82 - Fireball/Watergun/Vine

Triforce to klasyczny codémon, zawsze gotowy do walki. Ten wykorzystuje swoją siłę psychiczną do kontrolowania trzech żywiołów i wyrządzania jak największej szkody.


Możesz pobrać zespół tutaj:

Linux:

http://dl.free.fr/iHYlmTOQ2

uruchomić z ./Error310TMR

Windows:

http://dl.free.fr/vCyjtqo2s

uruchomić z ./Error310TMR.exe

Kod jest kompletnym projektem c ++. Nie wiem jak to opublikować.

$ wc -l src/*
    165 src/BruteForce.cpp
     26 src/BruteForce.h
    349 src/Codemon.cpp
     77 src/Codemon.h
     21 src/Logger.cpp
     35 src/Logger.h
    105 src/NoTimeToExplain.cpp
     27 src/NoTimeToExplain.h
    240 src/Recoverator.cpp
     31 src/Recoverator.h
     26 src/StrManip.cpp
     16 src/StrManip.h
    303 src/Team.cpp
     68 src/Team.h
     88 src/TooManyRedirects.cpp
     24 src/TooManyRedirects.h
     87 src/Unrecoverable.cpp
     27 src/Unrecoverable.h
     59 src/enums.cpp
    119 src/enums.h
     68 src/main.cpp
   1961 total

Ale jest bardzo skuteczny:

------- Final Results -------

176     Error310TMR
131     H3C
130     LittleKid
121     Nodemon
58      InsideYourHead
47      HardenedTrio
37      BitterRivals

2

Bajka

Dość ogólny zespół na środku drogi. Oparty na trzech archetypach, które starają się robić swoje, i wyłączają się, jeśli w ogóle nie mogą tego zrobić.

Ten zespół powstał, zanim wydawało się, że trucizna jest nową metą. Naprawdę nie nadążałem ze zmianą mojego zespołu, odkąd go stworzyłem, większość czasu spędziłem na próbowaniu parsowania. Nie udało mi się uruchomić Turnieju, aby go przetestować, ale był to dość pracowity tydzień. Jeśli ten nie działa tak, jak się wydaje, z moimi danymi testowymi, po pracy nadaję mu więcej blasku.

#!/bin/perl
use 5.20.0;
use strict;

use constant MINE => 0;
use constant THEIRS => 1;

$_ = $ARGV[0];

if(/^T/){
    say 'FairyTale|Fairy:1:89:90:51:3:4:13|Dragon:2:100:50:80:6:1:8|Assassin:0:70:60:100:0:1:10';

} elsif(/^C#(.*)/){
    my $state = readBattleState($1);
    if($state->[MINE]->{$state->[MINE]->{slot}}{hp}){
        say $state->[MINE]->{slot};
    } elsif($state->[MINE]->{($state->[MINE]->{slot}+1)%3}{hp}){
        say (($state->[MINE]->{slot}+1)%3);
    } else {
        say (($state->[MINE]->{slot}+2)%3);
    }

} elsif(/^A#(.*)/){
    my $state = readBattleState($1);
    my @actives = (
        $state->[MINE]->{$state->[MINE]->{slot}},
        $state->[THEIRS]->{$state->[THEIRS]->{slot}}
    );
    if($state->[MINE]->{slot} == 0){
        if(!exists($actives[THEIRS]{effects}{4}) && $actives[MINE]{pp}->[1]){
            say 1;
        } elsif(!exists($actives[THEIRS]{effects}{1}) && $actives[MINE]{pp}->[2]) {
            say 2;
        } elsif(!$actives[THEIRS]{type}) {
            if($state->[MINE]->{($state->[MINE]->{slot}+1)%3}{hp} > 0){
                say (($state->[MINE]->{slot}+1)%3);
            } elsif($state->[MINE]->{($state->[MINE]->{slot}+2)%3}{hp} > 0) {
                say (($state->[MINE]->{slot}+2)%3);
            } else {
                say 0;
            }
        } else {
            say 0;
        }
    } elsif($state->[MINE]->{slot} == 1){
        if(!exists($actives[MINE]{effects}{6}) && $actives[MINE]{pp}->[2]){
            say 2;
        } elsif ($actives[MINE]{hp} > 10 && $actives[MINE]{hp} < 50 && $actives[MINE]{pp}->[1]){
            say 1;
        } else {
            say 0;
        }
    } elsif($state->[MINE]->{slot} == 2){
        if(!exists($actives[MINE]{effects}{6}) && $actives[MINE]{pp}->[2]){
            say 2;
        } elsif ($actives[MINE]{hp} > 10 && $actives[MINE]{hp} < 50 && $actives[MINE]{pp}->[1]){
            say 1;
        } elsif($actives[THEIRS]{type} == 1) {
            if($state->[MINE]->{($state->[MINE]->{slot}+1)%3}{hp} > 0){
                say (($state->[MINE]->{slot}+1)%3);
            } elsif($state->[MINE]->{($state->[MINE]->{slot}+2)%3}{hp} > 0) {
                say (($state->[MINE]->{slot}+2)%3);
            } else {
                say 0;
            }
        } else {
            say 0;
        }
    }

} elsif(/^B#(.*)/){
    my $state = readTeam($1, 1);
    say '1:0:2';
}

sub readBattleState {
    local $_ = $_[0];
    if(/^(.*?)#(.*?)$/){
        my @teams;
        $teams[0] = readTeam($1, 1);
        $teams[1] = readTeam($2, 0);
        return \@teams;
    }
}

sub readTeam {
    my $isMine = $_[1];
    local $_ = $_[0];
    if(/.*?:(?<slot>.*?)\|(.*?)\|(.*?)\|(.*?)$/){
        my %team;
        $team{slot} = $1;
        $team{0} = $isMine ? readYourMember($2) : readTheirMember($2);
        $team{1} = $isMine ? readYourMember($3) : readTheirMember($3);
        $team{2} = $isMine ? readYourMember($4) : readTheirMember($4);
        return \%team;
    }
    return 0;
}

sub readYourMember {
    local $_ = $_[0];
    if(/(?<name>.*?):(?<id>.*?):(?<atk>.*?):(?<def>.*?):(?<spd>.*?):(?<hp>.*?):(?<type>.*?):(?<poison>.*?):(?<move0>.*?):(?<move1>.*?):(?<move2>.*?):(?<batk>.*?):(?<bdef>.*?):(?<bspd>.*?)(?<effects>(?::.*)|$)/){
        my %effects = map { $_ => 1 } readEffects($+{effects});
        my %member = (
            name   => $+{name},
            id     => $+{id},
            hp     => $+{hp},
            atk    => $+{atk}+$+{batk},
            def    => $+{def}+$+{bdef},
            spd    => $+{spd}+$+{bspd},
            type   => $+{type},
            pp     => [$+{move0}, $+{move1}, $+{move2}],
            poistrn=> $+{poison},
            effects=> \%effects
        );
        return \%member;
    }
}

sub readTheirMember {
    local $_ = $_[0];
    if(/(?<name>.*?):(?<id>.*?):(?<atk>.*?):(?<def>.*?):(?<spd>.*?):(?<hp>.*?):(?<type>.*?):(?<poison>.*?)(?<effects>(?::.*)|$)/){
        my %effects = map { $_ => 1 } readEffects($+{effects});
        my %member = (
            name   => $+{name},
            id     => $+{id},
            hp     => $+{hp},
            atk    => $+{atk},
            def    => $+{def},
            spd    => $+{spd},
            type   => $+{type},
            poistrn=> $+{poison},
            effects=> \%effects
        );
        return \%member;
    }
    return 0;
}

sub readEffects {
    local $_ = $_[0];
    my @retval = /:([^:]*)/g;
    if(!@retval){
        @retval = (0);
    }
    return @retval;
}

Biegnij z

perl fairytale.pl

Ten bot zostaje wyrzucony za próbę „wyboru aktywnych” zmarłych członków po pierwszej śmierci. Nigdy nie mija pierwszej bitwy, którą widzę.
Geobity

Naprawdę? Myślałem, że wcześniej udało mi się rozwiązać ten problem, może to jest starsza wersja tego kodu ...
mezzoEmrys

Nie zapominaj, że gdy nokaut zostanie znokautowany, jego punkty zdrowia spadną o 0 lub mniej . Zatem if($state->[MINE]->{$state->[MINE]->{slot}}{hp})instrukcja nie będzie działać bez >0.
GholGoth21

Ach, tak, to by to zrobiło.
mezzoEmrys

0

Dummy Team - Java

(niekonkurencyjny CW)

To jest atrapa zespołu do trenowania. Są to wszystkie normalne typy, które wybierają losowo między ruchami (cios, uzdrawianie, spowolnienie) w każdej turze. Baw się dobrze.

public class DummyTeam {

    public static void main(String[] args) {
        if(args.length < 1){
            System.out.println("You need to run this from the tournament. Try to keep up.");
            System.exit(0);
        }

        String[] sections = args[0].split("#");
        String out = "";
        switch(sections[0]){
            // team data
            //      sends back all Normal types with minimum stats and Normal moves (randomized name suffixes to distinguish in tests)
            case "T":
                out = "DummyTeam";
                for(int i=0;i<3;i++)
                    out += "|Dummy"+((char)(Math.random()*26)+65) + i + ":0:50:50:50:0:1:2";
                break;
            // bonus points
            //      shoves them all in defense every time
            case "B":
                out = "1:1:1";
                break;
            // choose active
            //      picks last active if alive, otherwise loops to find first living member
            case "C":
                String[] team = sections[1].split("\\|");
                int current = Integer.parseInt(team[0].split(":")[1]);
                if(Integer.parseInt(team[current+1].split(":")[5]) > 0){
                    out = current + "";
                } else {
                    for(int i=1;i<team.length;i++){
                        if(Integer.parseInt(team[i].split(":")[5]) > 0){
                            out = (i - 1) + "";
                        }
                    }
                }               
                break;
            // choose action
            //      chooses a random move. does not check if it ran out of uses, so wastes turns quite often
            case "A":
                out = ((int)(Math.random()*3)) + "";
                break;
            default:
                out = "Invalid query from controller.";             
        }
        System.out.println(out);
    }


}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.