Jak poprawnie zaprojektować wywołania zwrotne?

Odpowiedz Nowy wątek
2019-08-20 17:12
0

Gra "Snake" (mniej lub bardziej klasyczna wersja). Sytuacja wyjściowa jest taka:

  • Wąż to obiekt snake – przechowuje zbiór segmentów węża.
  • Segment węża to obiekt snakeSegment – składa się z aktualnej pozycji oraz aktualnego kierunku (tzn. takiego, "w którym idzie").
  • "Pozycja skrętu" jest to obiekt breakPoint (nie gańcie mnie za tę roboczą nazwę) – składa się z pary (pozycja, kierunek). Oznacza pozycję, w której dany segment węża musi skręcić w określonym kierunku (ponieważ został naciśnięty klawisz).
  • Pozycje skrętu są przechowywane w obiekcie breakPoints (zawiera ich tablicę).
  • Wąż może zbierać punkty – feed – które są przechowywane w obiekcie feeds (zawiera ich tablicę). UPDATE: Obiekt feed (ten bez "s") składa się z pozycji oraz wartości (ile punktów jest do zebrania w tym punkcie... eee... wiadomo, o co chodzi).

Podczas gry:

  • Wybór nowej pozycji jest ograniczony wielkością planszy oraz pozycjami innych segmentów węża.
  • Wybór (czy "wytyczenie"?) nowego kierunku dla każdego z segmentów jest ograniczony tym, czy istnieje dla danej pozycji segmentu punkt skrętu.

I teraz kod ruchu w obiekcie węża wygląda tak:

exports.snake = {
    ...,
    move: function (
        isPositionForbidden, // wywołanie zwrotne; parametr – position (tzn. "pozycja węża")
        doWhenPositionForbidden, // wywołanie zwrotne; parametr – position (tzn. "pozycja węża")
        isBreakPointPosition, // wywołanie zwrotne; parametr – segmentPosition (tzn. pozycja tego segmentu węża, dla którego ta funkcja jest wywoływana)
        getNewDirectionWhenBreakPointPosition, // wywołanie zwrotne; parametr – segmentPosition (tzn. pozycja tego segmentu węża, dla którego ta funkcja jest wywoływana)
        isFeedPosition, // wywołanie zwrotne; parametr – position (tzn. "pozycja węża")
        doWhenFeedPosition // wywołanie zwrotne; parametr – position (tzn. "pozycja węża")
    ) {
        ...
    },
    ...
};

Wywoływane jest to w głównej funkcji programu. Jak widać, są tu trzy grupy:

  1. sprawdzanie, czy wąż wyszedł poza tablicę lub wszedł na samego siebie, oraz obsługa sytuacji, jeśli tak;
  2. sprawdzanie, czy poszczególne segmenty natrafiły na pozycję skrętu, oraz obsługa sytuacji, jeśli tak;
  3. sprawdzanie, czy wąż natrafił na punkt, oraz obsługa sytuacji, jeśli tak.

Założeniem jest, że wąż nie wie nic o planszy, pozycjach skrętu ani punktach do zebrania. Zna jedynie swoje segmenty. Podobnież, segment nie wie nic o tych trzech rzeczach – zna jedynie swoją pozycję oraz kierunek. Dopiero na poziomie pozycji można określić, czy jest ona "niedozwolona" (forbidden), czy jest pozycją skrętu, oraz jest na niej punkt do zebrania.

Problemem jest to, że jest to 6 wywołań zwrotnych zawierających trzy grupy. Próbowałem, ale nie mam pomysłu, jak to można zapisać, hm... w bardziej skonsolidowany sposób? Inaczej? Przenieść może jakąś część logiki dokądś?


PS. Przy czym, jak widać, funkcja obsługi pozycji skrętu musi zwracać nowy kierunek – co powiększa problem, ponieważ burzy jednolitość tych trzech grup.


PS2: Pytajcie, jak coś niejasno napisałem. Nad taką logiką myślałem kilka dni, więc jest ona, patrząc z mojej strony, sophisticated.


edytowany 14x, ostatnio: Silv, 2019-08-20 17:35
Sam nie wiem, czy to bardziej nie nadaje się do działu "Inżynieria oprogramowania". - Silv 2019-08-20 17:22

Pozostało 580 znaków

2019-08-21 15:23
1

Jak już się bawimy w filozofowanie i kombinacje, to mam jeszcze jeden pomysł na realizację Węża. Całość opiera się na następujących zmiennych:

  • tablicy komórek, każda komórka może zawierać:
    • pustkę: 0,
    • ziarenko do zjedzenia: -1,
    • ogniwo węża: liczba milisekund uniksowych opisujących czas narodzin danej komórki,
  • współrzędnych głowy i ogona,
  • aktualnym kierunku poruszania.

W każdym kolejnym kroku:

  • w oparciu o współrzędne głowy i kierunek poruszania

    • wyliczasz gdzie przesunie się głowa
    • testujesz ew. kolizje,
    • wypełniasz kolejną komórkę ogniwem węża,
  • w oparciu o współrzędne ogona:

    • zdejmujesz z planszy czubek ogona,
    • wyszukujesz współrzędne nowego czubka ogona, sprawdzając 8 komórek sąsiadujących z właśnie wyczyszczoną, wybierasz tą, która zawiera najmniejszą liczbę większą od zera.

Przy czym wskazane byłoby odwrócić kolejność i najpierw usuwać ogon i dopiero wtedy testować kolizje i rysować głowę.

Trochę cudactwo, ale jeśli i tak chcesz tam robić jakąś tablicę komórek, to nie potrzebujesz drugiej osobnej na samego węża :)


Po co wyszukiwać współrzędne nowego czubka ogona?... - Silv 2019-08-21 17:19
Po to, żebyś w następnym kroku wiedział, gdzie on się znajduje i wiedział, co masz usunąć. - Freja Draco 2019-08-21 18:56
Ach, no tak. Bo obecnie przechowuję listę segmentów. Intuicja, intuicja... - Silv 2019-08-21 19:03

Pozostało 580 znaków

2019-08-23 04:23
1

Ostatecznie przemodelowałem architekturę programu, niemniej wywołania zwrotne zostały zmienione jedynie lekko.

Zdecydowałem się jednak porzucić projekt. W związku z tym, wątek pozostawiam bez zaakceptowanej odpowiedzi.

Niemniej, bardzo pomogła mi wskazówka @Freja Draco w tym poście: https://4programmers.net/Forum/1613705


edytowany 1x, ostatnio: Silv, 2019-08-23 04:23
A proszę :) - Freja Draco 2019-08-23 13:51

Pozostało 580 znaków

2019-08-23 06:12
1

Najprościej to po prostu w każdym ruchu wstawić nową głowę i usunąć ogon. Jeśli wąż zeżarł jabłko to tylko wstawić głowę. Wszystkie dane w segmentach inne niż współrzędne (dla głowy dodatkowo kierunek) oraz czynności inne niż opisane w pierwszym zdaniu są absolutnie zbędne. ;)

Kiedyś robiłem konsolowego węża, ale z bardziej skomplikowaną mechaniką. Oprócz zwykłego poruszania się i zjadania jabłek, można było przechodzić przez segmenty, w których znajdowało się jabłko (były renderowane jaśniejszym kolorem, żeby było wiadomo gdzie są). Obsługa punktów przecięcia węża znacznie uatrakcyjniała rozgrywkę.


edytowany 8x, ostatnio: furious programming, 2019-08-23 15:38

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0