Rozwiązując problem sieciowy , możesz stworzyć silnik reguł, w którym każda konkretna reguła jest kodowana niezależnie. Kolejnym udoskonaleniem tego byłoby utworzenie języka specyficznego dla domeny (DSL) w celu utworzenia reguł, jednak sama DSL przesuwa problem tylko z jednej bazy kodu (głównej) na inną (DSL). Bez struktury DSL nie poradzi sobie lepiej niż język ojczysty (Java, C # itp.), Więc wrócimy do niego po znalezieniu ulepszonego podejścia strukturalnego.
Podstawową kwestią jest to, że masz problem z modelowaniem. Ilekroć napotkasz takie sytuacje kombinatoryczne, jest to wyraźny znak, że twoja abstrakcja modelu opisująca sytuację jest zbyt gruba. Najprawdopodobniej łączysz elementy, które powinny należeć do różnych modeli w jednym obiekcie.
Jeśli nadal będziesz rozkładać swój model, ostatecznie całkowicie rozpuścisz ten efekt kombinatoryczny. Jednak idąc tą ścieżką łatwo jest zgubić się w projekcie, tworząc jeszcze większy bałagan, perfekcjonizm tutaj niekoniecznie jest twoim przyjacielem.
Maszyny stanów skończonych i silniki reguł są tylko przykładem tego, jak można rozwiązać ten problem i uczynić go łatwiejszym do zarządzania. Główną ideą tutaj jest to, że dobrym sposobem na pozbycie się takiego problemu kombinatorycznego jest często stworzenie projektu i powtórzenie go ad-nudności w zagnieżdżonych poziomach abstrakcji, aż system osiągnie zadowalające wyniki. Podobnie jak fraktale są używane do tworzenia skomplikowanych wzorów. Zasady pozostają takie same, bez względu na to, czy patrzysz na swój system za pomocą mikroskopu, czy z lotu ptaka.
Przykład zastosowania tego w swojej domenie.
Próbujesz modelować ruch krów w terenie. Chociaż w twoim pytaniu brakuje szczegółów, sądzę, że twoja duża liczba ifs zawiera fragment decyzji, na przykład, if cow.isStanding then cow.canRun = true
ale utkniesz w trakcie dodawania szczegółów terenu. Dlatego dla każdego działania, które chcesz podjąć, musisz sprawdzić wszystkie aspekty, które możesz wymyślić, i powtórzyć te weryfikacje dla następnego możliwego działania.
Najpierw potrzebujemy naszego powtarzalnego projektu, którym w tym przypadku będzie FSM do modelowania zmieniających się stanów symulacji. Pierwszą rzeczą, którą bym zrobił, to zaimplementować referencyjny FSM, definiujący interfejs stanu, interfejs przejścia i być może kontekst przejściaktóry może zawierać wspólne informacje, które zostaną udostępnione pozostałym dwóm. Podstawowa implementacja FSM będzie się przełączać z jednego przejścia na drugie, niezależnie od kontekstu, tutaj pojawia się silnik reguł. Silnik reguł dokładnie określa warunki, które należy spełnić, aby przejście miało nastąpić. Tutaj silnik reguł może być tak prosty jak lista reguł, z których każda ma funkcję oceny zwracającą wartość logiczną. Aby sprawdzić, czy przejście powinno nastąpić, dokonujemy iteracji listy reguł, a jeśli którakolwiek z nich ma wartość false, przejście nie ma miejsca. Samo przejście będzie zawierało kod behawioralny służący do modyfikacji bieżącego stanu FSM (i innych możliwych zadań).
Teraz, jeśli zacznę implementować symulację jako pojedynczy duży FSM na poziomie BOGA, otrzymam DUŻO możliwych stanów, przejść itp. Bałagan jeśli-inaczej wygląda na naprawiony, ale tak naprawdę rozprzestrzenia się: każdy IF jest teraz reguła, która wykonuje test pod kątem konkretnej informacji kontekstu (która w tym momencie zawiera prawie wszystko), a każda treść JEŻELI znajduje się gdzieś w kodzie przejścia.
Wprowadź podział fraktali: pierwszym krokiem byłoby utworzenie FSM dla każdej krowy, w której stany są stanami wewnętrznymi krowy (stojąca, biegająca, chodząca, pasąca się itp.), A środowisko będzie miało wpływ na przejścia między nimi. Możliwe, że wykres nie jest kompletny, na przykład wypas jest dostępny tylko ze stanu stojącego, wszelkie inne przejścia są odrzucane, ponieważ po prostu nie ma go w modelu. Tutaj skutecznie dzielisz dane na dwa różne modele, krowę i teren. Każdy z własnym zestawem właściwości. Podział ten pozwoli uprościć ogólny projekt silnika. Teraz zamiast jednego silnika reguł, który decyduje o wszystkim, masz wiele, prostszych mechanizmów reguł (po jednym dla każdej przejścia), które decydują o bardzo szczegółowych szczegółach.
Ponieważ ponownie używam tego samego kodu dla FSM, jest to w zasadzie konfiguracja FSM. Pamiętasz, jak wspominaliśmy wcześniej o DSL? W tym miejscu DSL może wiele zdziałać, jeśli masz dużo reguł i przejść do napisania.
Schodzę głębiej
Teraz BÓG nie musi już radzić sobie z całą złożonością zarządzania wewnętrznymi stanami krowy, ale możemy to zrobić dalej. Na przykład zarządzanie terenem jest nadal bardzo skomplikowane. Tutaj decydujesz, gdzie wystarczy rozbicie. Jeśli na przykład w swoim BOGU skończysz zarządzanie dynamiką terenu (długa trawa, błoto, suche błoto, krótka trawa itp.), Możemy powtórzyć ten sam wzór. Nic nie stoi na przeszkodzie, aby wprowadzić taką logikę w sam teren poprzez wyodrębnienie wszystkich stanów terenu (długa trawa, krótka trawa, błotnista, sucha itp.) Do nowego terenu FSM z przejściami między stanami i być może prostymi regułami. Na przykład, aby przejść do stanu błotnistego, silnik reguł powinien sprawdzić kontekst w celu znalezienia płynów, w przeciwnym razie nie jest to możliwe. Teraz BÓG stał się jeszcze prostszy.
Możesz uzupełnić system FSM, czyniąc go autonomicznym i nadając każdemu wątek. Ten ostatni krok nie jest konieczny, ale umożliwia dynamiczną zmianę interakcji systemu poprzez dostosowanie sposobu delegowania decyzji (uruchomienie specjalistycznego FSM lub po prostu przywrócenie wcześniej określonego stanu).
Pamiętasz, jak wspomnieliśmy, że przejścia mogą również wykonywać „inne możliwe zadania”? Zbadajmy to, dodając możliwość komunikacji między różnymi modelami (FSM). Możesz zdefiniować zestaw zdarzeń i zezwolić każdemu FSM na zarejestrowanie odbiornika tych zdarzeń. Tak więc, jeśli na przykład krowa wchodzi na heks terenu, heks może zarejestrować słuchaczy pod kątem zmian przejścia. Tutaj staje się to nieco trudne, ponieważ każdy FSM jest wdrażany na bardzo wysokim poziomie bez wiedzy o konkretnej domenie, w której się znajduje. Możesz to jednak osiągnąć poprzez opublikowanie przez krowę listy zdarzeń, a komórka może się zarejestrować, jeśli zobaczy zdarzenia, na które może zareagować. Dobra hierarchia rodziny wydarzeń tutaj jest dobrą inwestycją.
Możesz pchać jeszcze głębiej, modelując poziomy składników odżywczych i cykl wzrostu trawy, za pomocą ... zgadłeś ... FSM trawy osadzonej we własnym modelu łaty terenu.
Jeśli popchniecie tę ideę wystarczająco daleko, BÓG ma bardzo niewiele do zrobienia, ponieważ wszystkie aspekty są właściwie samozarządzane, uwalniając czas na wydawanie na bardziej pobożne rzeczy.
Podsumować
Jak wspomniano powyżej, FSM nie jest tutaj rozwiązaniem, a jedynie środkiem do zilustrowania, że rozwiązania takiego problemu nie ma w kodzie, powiedzmy, w jaki sposób modelujesz swój problem. Najprawdopodobniej istnieją inne rozwiązania, które są możliwe i najprawdopodobniej znacznie lepsze niż moja propozycja FSM. Jednak podejście „fraktali” pozostaje dobrym sposobem radzenia sobie z tą trudnością. Jeśli zrobisz to poprawnie, możesz dynamicznie alokować głębsze poziomy tam, gdzie ma to znaczenie, jednocześnie dając prostsze modele tam, gdzie ma to mniejsze znaczenie. Możesz kolejkować zmiany i stosować je, gdy zasoby staną się bardziej dostępne. W sekwencji akcji może nie być aż tak ważne obliczenie transferu składników pokarmowych z krowy na trawę. Możesz jednak zarejestrować te przejścia i zastosować zmiany w późniejszym czasie lub w przybliżeniu z wyuczonym odgadnięciem, po prostu zastępując mechanizmy reguł lub ewentualnie zastępując implementację FSM prostszą naiwną wersją dla elementów, które nie znajdują się w bezpośrednim polu zainteresowanie (ta krowa na drugim końcu pola), aby umożliwić bardziej szczegółowe interakcje, aby uzyskać koncentrację i większy udział zasobów. Wszystko to bez ponownego przeglądania systemu jako całości; ponieważ każda część jest dobrze odizolowana, łatwiej jest stworzyć zastępczą wymianę ograniczającą lub rozszerzającą głębokość modelu. Korzystając ze standardowego projektu, możesz na tym oprzeć i zmaksymalizować inwestycje w narzędzia ad-hoc, takie jak DSL, w celu zdefiniowania reguł lub standardowego słownictwa dla wydarzeń, ponownie zaczynając od bardzo wysokiego poziomu i dodając udoskonalenia w razie potrzeby. ponieważ każda część jest dobrze odizolowana, łatwiej jest stworzyć zastępczą wymianę ograniczającą lub rozszerzającą głębokość modelu. Korzystając ze standardowego projektu, możesz na tym oprzeć i zmaksymalizować inwestycje w narzędzia ad-hoc, takie jak DSL, w celu zdefiniowania reguł lub standardowego słownictwa dla wydarzeń, ponownie zaczynając od bardzo wysokiego poziomu i dodając udoskonalenia w razie potrzeby. ponieważ każda część jest dobrze odizolowana, łatwiej jest stworzyć zastępczą wymianę ograniczającą lub rozszerzającą głębokość modelu. Korzystając ze standardowego projektu, możesz na tym oprzeć i zmaksymalizować inwestycje w narzędzia ad-hoc, takie jak DSL, w celu zdefiniowania reguł lub standardowego słownictwa dla wydarzeń, ponownie zaczynając od bardzo wysokiego poziomu i dodając udoskonalenia w razie potrzeby.
Podałbym przykład kodu, ale to wszystko, co mogę teraz zrobić.