To dwutygodniowe wyzwanie nr 3. Temat: Algorytmy genetyczne
To wyzwanie jest trochę eksperymentem. Chcieliśmy zobaczyć, co możemy zrobić, pod kątem wyzwań, za pomocą algorytmów genetycznych. Nie wszystko może być optymalne, ale staraliśmy się, aby było to dostępne. Jeśli to się powiedzie, kto wie, co możemy zobaczyć w przyszłości. Może genetyczny król wzgórza?
Specyfikacja jest dość długa! Próbowaliśmy podzielić specyfikację na Podstawy - absolutne minimum, które musisz wiedzieć, aby zacząć grać z frameworkiem i przesłać odpowiedź - oraz The Gory Details - pełną specyfikację ze wszystkimi szczegółami na temat kontrolera, na podstawie których mógłbym napisać własny.
Jeśli masz jakiekolwiek pytania, dołącz do nas na czacie!
Jesteś badaczem psychologii behawioralnej. Jest piątek wieczorem, a ty i twoi koledzy decydujecie się zabawić i wykorzystać swoje szczury laboratoryjne do małego wyścigu szczurów. W rzeczywistości, zanim przywiążemy się do nich zbyt emocjonalnie, nazwijmy je okazami .
Przygotowałeś mały tor wyścigowy dla okazów, a żeby był bardziej interesujący, umieściłeś na nim kilka ścian, pułapek i teleporterów. Teraz twoje okazy są nadal szczurami ... nie mają pojęcia, czym jest pułapka lub teleporter. Widzą tylko niektóre rzeczy w różnych kolorach. Nie mają też żadnej pamięci - wszystko, co mogą zrobić, to podejmować decyzje w oparciu o ich obecne otoczenie. Myślę, że dobór naturalny wyodrębni okazy, które potrafią uniknąć pułapki od tych, które tego nie robią (ten wyścig zajmie trochę czasu ...). Niech rozpocznie się gra! †
† 94 465 okazów zostało poszkodowanych przy podejmowaniu tego wyzwania.
Podstawy
Jest to gra dla jednego gracza (ty i twoi koledzy nie chcieliście mieszać populacji, więc każdy zbudował własny tor wyścigowy). Tor wyścigowy jest prostokątną siatką o wysokości 15 komórek i szerokości 50 komórek. Zaczynasz z 15 okazami na losowych (niekoniecznie odrębnych) komórkach na lewej krawędzi (gdzie x = 0 ). Próbki powinny próbować osiągnąć cel, którym jest dowolna komórka przy x ≥ 49 i 0 ≤ y ≤ 14 (okazy mogą przekroczyć ścieżkę w prawo). Za każdym razem, gdy tak się dzieje, dostajesz punkt. Grę rozpoczynasz również z 1 punktem. Powinieneś spróbować zmaksymalizować swoje punkty po 10 000 tur.
Wiele próbek może zajmować tę samą komórkę i nie będzie oddziaływać.
Na każdym kroku każdy okaz widzi siatkę 5x5 swojego otoczenia (z samym sobą na środku). Każda komórka tej siatki będzie zawierała kolor -1
do 15
. -1
reprezentuje komórki, które są poza granicami. Twój okaz umrze, jeśli wyjdzie poza granice. Jeśli chodzi o inne kolory, reprezentują puste komórki, pułapki, ściany i teleportery. Ale twój okaz nie wie, który kolor reprezentuje to, co ty, i ty też. Istnieją jednak pewne ograniczenia:
- 8 kolorów będzie reprezentować puste komórki.
- 4 kolory będą reprezentować teleporter. Teleporter wyśle próbkę do określonej komórki w jej sąsiedztwie 9x9. To przesunięcie będzie takie samo dla wszystkich teleporterów tego samego koloru.
- 2 kolory będą reprezentować ściany. Poruszanie się w ścianie jest tym samym, co stanie w bezruchu.
- 2 kolory będą stanowić pułapkę. Pułapka wskazuje, że jedna z 9 komórek w jej bezpośrednim sąsiedztwie jest śmiertelna (niekoniecznie sama komórka pułapki). To przesunięcie będzie takie samo dla wszystkich pułapek tego samego koloru.
O tej naturalnej selekcji ... każdy okaz ma genom, który jest liczbą 100 bitów. Nowe okazy zostaną utworzone przez krzyżowanie dwóch istniejących okazów, a następnie nieznaczną mutację genomu. Im bardziej udany okaz, tym większa szansa na jego rozmnażanie.
Oto twoje zadanie: napiszesz jedną funkcję, która otrzymuje jako dane wejściowe siatkę kolorów 5x5, którą widzi próbka, a także jej genom. Twoja funkcja zwróci ruch (Δx, yy) dla próbki, gdzie Δx i Δy będą jednym z nich {-1, 0, 1}
. Nie wolno utrwalać żadnych danych między wywołaniami funkcji. Obejmuje to używanie własnych generatorów liczb losowych. Twoja funkcja otrzyma rozstawiony RNG, z którego możesz dowolnie korzystać.
Wynik Twojego zgłoszenia będzie średnią geometryczną liczby punktów na 50 losowych ścieżkach. Stwierdziliśmy, że wynik ten jest dość zróżnicowany. Dlatego te wyniki będą wstępne . Kiedy to wyzwanie dobiegnie końca, zostanie ogłoszony termin. Po upływie terminu 100 losowo wybieranych jest 100 plansz, a wszystkie zgłoszenia zostaną zapisane na tych 100 planszach. Możesz wpisać szacunkowy wynik w odpowiedzi, ale sami ocenimy każde zgłoszenie, aby nikt nie oszukiwał.
Udostępniliśmy programy kontrolera w kilku językach. Obecnie możesz napisać swoje zgłoszenie w języku Python (2 lub 3), Ruby , C ++ , C # lub Java . Kontroler generuje plansze, uruchamia grę i zapewnia ramy dla algorytmu genetycznego. Wszystko, co musisz zrobić, to zapewnić funkcję ruchu.
Czekaj, więc co dokładnie robię z genomem?
Wyzwanie polega na zrozumieniu tego!
Ponieważ okazy nie mają pamięci, wszystko, co masz w danej turze, to siatka kolorów 5x5, które nic dla ciebie nie znaczą. Musisz więc użyć genomu, aby osiągnąć cel. Ogólna idea polega na tym, że używasz części genomu do przechowywania informacji o kolorach lub układzie siatki, a twój bot opiera swoje decyzje na dodatkowych informacjach przechowywanych w genomie.
Teraz oczywiście nie można ręcznie niczego tam przechowywać. Tak więc przechowywane tam rzeczywiste informacje będą początkowo całkowicie losowe. Ale algorytm genetyczny wkrótce wybierze te okazy, których genom zawiera właściwą informację, jednocześnie zabijając te, które mają niewłaściwą informację. Twoim celem jest znalezienie mapowania z fragmentów genomu i pola widzenia na ruch, który pozwala szybko znaleźć ścieżkę do celu i który konsekwentnie ewoluuje do zwycięskiej strategii.
To powinno wystarczyć do rozpoczęcia pracy. Jeśli chcesz, możesz pominąć następną sekcję i wybrać kontroler z listy kontrolerów u dołu (która zawiera również informacje o tym, jak używać tego konkretnego kontrolera).
Czytaj dalej, jeśli chcesz wszystko ...
Krwawe szczegóły
Ta specyfikacja jest kompletna. Wszyscy administratorzy muszą wdrożyć te reguły.
Wszelka losowość wykorzystuje rozkład równomierny, chyba że zaznaczono inaczej.
Generowanie torów:
- Ścieżka ma prostokątną siatkę, szerokość X = 53 komórki i wysokość Y = 15 komórek. Komórki zx ≥ 49 są komórkami docelowymi (gdzie x jest zerowy).
- Każda komórka ma jeden kolor i może, ale nie musi, być śmiertelna - komórki nie są śmiertelne, chyba że określi je jeden z poniższych typów komórek.
- Istnieje 16 różnych kolorów komórek, oznaczonych od
0
do15
, których znaczenie zmieni się z gry na grę. Ponadto-1
reprezentuje komórki, które są poza granicami - są śmiertelne . - Wybierz 8 losowych kolorów . Będą to puste komórki (które nie działają).
- Wybierz 4 więcej losowych kolorów . To są teleportery. Dla dwóch z tych kolorów wybierz niezerowe przesunięcie w sąsiedztwie 9x9 (od (-4, -4) do (4,4) z wyjątkiem (0,0)). Dla pozostałych dwóch kolorów odwróć te przesunięcia. Jeżeli próbka nadepnie na teleporter, zostanie natychmiast przesunięta o to przesunięcie.
- Wybierz jeszcze 2 losowe kolory . To są pułapki. Dla każdego z tych kolorów wybierz przesunięcie w sąsiedztwie 3x3 (od (-1, -1) do (1,1)). Pułapka wskazuje, że komórka w tym przesunięciu jest śmiertelna . Uwaga: Sama komórka pułapkowa niekoniecznie jest śmiertelna.
- Do 2 Pozostałe kolory są ściany, które utrudniają ruch. Próba przejścia na komórkę ścienną zmieni ruch w nieruchomy. Same komórki ścienne są śmiertelne .
- Dla każdej komórki niebędącej celem siatki wybierz losowy kolor. Dla każdej komórki celu wybierz losowy pusty kolor.
- Dla każdej komórki na lewej krawędzi toru ustal, czy cel można osiągnąć w ciągu 100 obrotów (zgodnie z poniższymi zasadami kolejności zwrotów ). Jeśli tak, ta komórka jest dopuszczalną komórką początkową . Jeśli jest mniej niż 10 komórek początkowych, odrzuć ścieżkę i wygeneruj nową.
- Utwórz 15 okazów, każdy z losowym genomem i wieku 0 . Umieść każdą próbkę w losowej początkowej komórce.
Kolejność:
- Dla każdej próbki zostaną wykonane kolejno następujące kroki. Okazy nie wchodzą w interakcje ani nie widzą się nawzajem i mogą zajmować tę samą komórkę.
- Jeśli wiek okazu wynosi 100 , umiera. W przeciwnym razie zwiększ jego wiek o 1.
- Próbka otrzymuje swoje pole widzenia - siatkę kolorów 5x5, wyśrodkowaną na próbce - i zwraca ruch w sąsiedztwie 3x3. Przesunięcie poza ten zakres spowoduje zakończenie pracy sterownika.
- Jeśli komórką docelową jest ściana, ruch zmienia się na (0,0).
- Jeśli komórką docelową jest teleporter, próbka zostaje przesunięta o przesunięcie teleportera. Uwaga: Ten krok jest wykonywany raz , a nie iteracyjnie.
- Jeśli komórka zajmowana obecnie przez próbkę (potencjalnie po użyciu jednego teleportera) jest śmiertelna, próbka umiera. Jest to jedyny czas, w którym próbki umierają (oprócz kroku 1.1. Powyżej). W szczególności nowy okaz, który odradza się w śmiertelnej komórce, nie umrze natychmiast, ale ma szansę na pierwsze zejście z niebezpiecznej komórki.
- Jeśli próbka zajmuje komórkę celu, zdobądź punkt, przenieś próbkę do losowej komórki początkowej i zresetuj jej wiek do 0.
- Jeśli na planszy pozostały mniej niż dwa okazy, gra się kończy.
- Utwórz 10 nowych próbek w wieku 0 lat . Każdy genom jest określany (indywidualnie) na podstawie poniższych reguł hodowlanych. Umieść każdą próbkę w losowej początkowej komórce.
Hodowla:
Po utworzeniu nowego okazu wybierz losowo dwóch różnych rodziców, z nastawieniem na osobniki, które posunęły się dalej w prawo. Prawdopodobieństwo wyboru próbki jest proporcjonalne do jej aktualnej oceny kondycji . Ocena kondycji okazu wynosi
1 + x + 50 * ile razy osiągnął cel
gdzie x jest wskaźnikiem poziomym opartym na 0. Okazy utworzone w tej samej turze nie mogą być wybrane jako rodzice.
Z dwojga rodziców wybierz losowego, z którego zostanie pobrany pierwszy bit genomu.
- Teraz, gdy idziesz wzdłuż genomu, przełączaj rodziców z prawdopodobieństwem 0,05 i nadal pobieraj kawałki od wynikowego rodzica.
- Zmutuj w pełni złożony genom: dla każdego bitu odwróć go z prawdopodobieństwem 0,01 .
Punktacja:
- Jedna gra trwa 10 000 tur.
- Gracze rozpoczynają grę z 1 punktem (aby umożliwić użycie średniej geometrycznej).
- Za każdym razem, gdy okaz osiąga cel, gracz zdobywa punkt.
- Na razie zgłoszenie każdego gracza zostanie uruchomione na 50 gier, każda z inną losową ścieżką.
- Powyższe podejście powoduje większą wariancję niż jest to pożądane. Kiedy to wyzwanie dobiegnie końca, zostanie ogłoszony termin. Po upływie terminu 100 losowo wybieranych jest 100 plansz, a wszystkie zgłoszenia zostaną zapisane na tych 100 planszach.
- Ogólny wynik gracza jest średnią geometryczną wyników poszczególnych gier.
Kontrolery
Możesz wybrać jeden z następujących kontrolerów (ponieważ są one funkcjonalnie równoważne). Przetestowaliśmy je wszystkie, ale jeśli zauważysz błąd, chcesz poprawić kod lub wydajność, lub dodasz funkcję graficzną, wyślij zgłoszenie problemu lub wyślij żądanie ściągnięcia na GitHub! Możesz również dodać nowy kontroler w innym języku!
Kliknij nazwę języka dla każdego kontrolera, aby przejść do właściwego katalogu na GitHub, który zawiera README.md
dokładne instrukcje użytkowania.
Jeśli nie znasz git i / lub GitHub, możesz pobrać całe repozytorium jako plik ZIP ze strony głównej (patrz przycisk na pasku bocznym).
Pyton
- Najbardziej dokładnie przetestowany. To jest nasza referencyjna implementacja.
- Działa zarówno z Python 2.6+, jak i Python 3.2+!
- Jest bardzo wolny. Zalecamy korzystanie z PyPy w celu znacznego przyspieszenia.
- Obsługuje wyjście graficznego korzystając albo
pygame
albotkinter
.
Rubin
- Testowane z Ruby 2.0.0. Powinien działać z nowszymi wersjami.
- Jest również dość powolny, ale Ruby może być dogodny do prototypowania pomysłu na przesłanie.
C ++
- Wymaga C ++ 11.
- Opcjonalnie obsługuje wielowątkowość.
- Zdecydowanie najszybszy kontroler w grupie.
DO#
- Używa LINQ, więc wymaga .NET 3.5.
- Raczej powolny.
Jawa
- Niezbyt wolno. Niezbyt szybko.
Wstępna tablica wyników
Wszystkie wyniki są wstępne. Jeśli jednak coś jest nie tak lub nieaktualne, daj mi znać. Nasze przykładowe przesłanie jest wymienione w celach porównawczych, ale nie jest niezgodne.
Score | # Games | User | Language | Bot
===================================================================================
2914.13 | 2000 | kuroi neko | C++ | Hard Believers
1817.05097| 1000 | TheBestOne | Java | Running Star
1009.72 | 2000 | kuroi neko | C++ | Blind faith
782.18 | 2000 | MT0 | C++ | Cautious Specimens
428.38 | | user2487951 | Python | NeighborsOfNeighbors
145.35 | 2000 | Wouter ibens | C++ | Triple Score
133.2 | | Anton | C++ | StarPlayer
122.92 | | Dominik Müller | Python | SkyWalker
89.90 | | aschmack | C++ | LookAheadPlayer
74.7 | | bitpwner | C++ | ColorFarSeeker
70.98 | 2000 | Ceribia | C++ | WallGuesser
50.35 | | feersum | C++ | Run-Bonus Player
35.85 | | Zgarb | C++ | Pathfinder
(34.45) | 5000 | Martin Büttner | <all> | ColorScorePlayer
9.77 | | DenDenDo | C++ | SlowAndSteady
3.7 | | flawr | Java | IAmARobotPlayer
1.9 | | trichoplax | Python | Bishop
1.04 | 2000 | fluffy | C++ | Gray-Color Lookahead
Kredyty
To wyzwanie było ogromnym wysiłkiem polegającym na współpracy:
- Nathan Merril: Napisałem kontrolery Python i Java. Przekształcił koncepcję wyzwania z King-of-the-Hill w wyścig szczurów.
- trichoplax: Playtesting . Pracował na kontrolerze Python.
- feersum: Napisałem kontroler C ++.
- VisualMelon: Napisałem kontroler C #.
- Martin Büttner: Koncepcja. Napisałem kontroler Ruby. Testowanie Pracował na kontrolerze Python.
- T Abraham: Playtesting. Przetestowałem Python i przejrzałem kontrolery C # i C ++.
Wszyscy powyżsi użytkownicy (i pewnie jeszcze kilku zapomniałem) przyczynili się do ogólnej konstrukcji wyzwania.
Aktualizacja kontrolera C ++
Jeśli używasz C ++ z Visual Studio i wielowątkowością, powinieneś otrzymać najnowszą aktualizację z powodu błędu w ich generowaniu liczb losowych, który pozwala na tworzenie duplikatów kart.
'In particular, a new specimen which spawns on a lethal cell will not die immediately, but has a chance to move off the dangerous cell first.'