Jak uniemożliwić ruch w przeciwną stronę w Snake'u?

Jak uniemożliwić ruch w przeciwną stronę w Snake'u?
Silv
Moderator Wiki
  • Rejestracja:ponad 10 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Warszawa
0

Krótko: mamy sobie grę Snake w JavaScript w Node.js, w konsoli. Klasycznie, poruszanie wężykiem za pomocą klawiszy strzałek. Plansza przeładowuje się cała za każdym posunięciem węża.

Założenie jest takie, że wąż nie może "zawracać w miejscu". I problem – przykładowo: jeśli wąż idzie w dół, a ja nacisnę szybko po sobie LEFT oraz UP, to program nie zdąży przeładować planszy i wyświetlić ruchu w lewo, tylko od razu wyświetla w górę.

Jak zrobić, żeby niezależnie od szybkości naciśnięcia po sobie tych klawiszy był wyświetlany zarówno ruch w lewo, jak i w górę?

To kod, który powinien się przydać (trochę go uprościłem, odrzucając abstrakcje):

Kopiuj
// Plik "run.js"
// Tu początek pliku

... // tu cośtam

process.stdin.on("data", (keyPressed) => {
            settings.snakeSettings = handleKeypress(
                keyPressed,
                settings.snakeSettings,
                exitGame
            );
        }
    });

const intervalID = timers.setInterval(() => {
    // W funkcji "makeStep" wywoływana jest funkcja "printBoard"
    const newSnakeSettings = makeStep(
        settings.environmentSettings,
        settings.gameSettings,
        settings.boardSettings,
        settings.snakeSettings,
        exitGame,
        computeNewSnakePosition,
        clearOutput,
        printBoard
    );
    settings.snakeSettings = newSnakeSettings;
}, settings.gameSettings.level);

// Tu koniec pliku

edytowany 8x, ostatnio: Silv
CR
  • Rejestracja:około 6 lat
  • Ostatnio:około godziny
  • Postów:112
2

Np. dać flagę, że wąż zmienił już kierunek i jeżeli tak, to uniemożliwić kolejną zmianę kierunku, po czym czyścic flagę po przeładowaniu planszy?

edytowany 1x, ostatnio: Crazy_Rockman
Silv
Moderator Wiki
  • Rejestracja:ponad 10 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Warszawa
0

Wiesz co, próbowałem tego, ale wydawało mi się, że program zbyt wolno reaguje na kolejne klawisze. Ale to chyba musi tak już być – trzeba po prostu poczekać z wciśnięciem... Ostatecznie lepsze nieobsłużone wciśnięcie klawisza niż zawracanie w miejscu.


PS. Gdyby jednak udało się coś wymyślić, żeby oba wciśnięcia były obsługiwane, byłoby fajnie.


edytowany 3x, ostatnio: Silv
BU
  • Rejestracja:około 10 lat
  • Ostatnio:11 dni
  • Postów:422
1
Silv napisał(a):

Założenie jest takie, że wąż nie może "zawracać w miejscu". I problem – przykładowo: jeśli wąż idzie w dół, a ja nacisnę szybko po sobie LEFT oraz UP, to program nie zdąży przeładować planszy i wyświetlić ruchu w lewo, tylko od razu wyświetla w górę.

Po naciśnięciu LEFT zmienia się kierunek i wąż nie powinien reagować na żaden inny przycisk, dopóki nie poruszy się o jedno pole. Wydaje mi się, że w tym przypadku przycisk UP powinien zostać zignorowany. Co by było, gdybyś nacisnął jeszcze więcej różnych przycisków? Według mnie tylko pierwszy naciśnięty w danym ruchu powinien być brany pod uwagę. W takim przypadku szybkie naciśnięcie LEFT i UP zadziała dla szybkiego węża tak jak chcesz.

edytowany 1x, ostatnio: Burmistrz
Silv
Moderator Wiki
  • Rejestracja:ponad 10 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Warszawa
0

@Burmistrz: widzisz, ale to stwarza konieczność czekania, aż minie te 500 czy 400 ms i dopiero wciśnięcia klawisza. Taki jakby lag.


PS. Jak udostępnię gierkę na GitHubie, będzie można się przekonać. ;) Na razie jeszcze nie widać w tym gry.


UPDATE. Wyobraź sobie: wciskasz dwa klawisze, a tu działa tylko pierwszy. Wąż pełźnie z zawrotną prędkością, zaraz koniec planszy, a Ty kombinujesz, dlaczego nie zmienił kierunku tak, jak chciałeś.


edytowany 3x, ostatnio: Silv
Zobacz pozostałe 2 komentarze
Silv
PS. Ciekawe, ile uda Ci się napisać w jeden dzień... jak całość (z powiększaniem się węża, zawijaniem itp.), to będzie godne podziwu.
BU
Nie doczytałem, że w konsoli robisz. Ja robiłem graficznie w trybie 640x480, a jutro spróbuję w JS z użyciem canvas (nigdy z tego nie korzystałem, ale chciałem spróbować). Możliwe, że nie zrobię w jeden dzień, ale jak będę się nudził na kacu i nie trafię na jakiś poważny problem, to raczej jakąś działającą wersję uda mi się zrobić ;)
Silv
A graficznie to nie w JS, pewnie w C++?
BU
Turbo Pascal 7.0
fasadin
  • Rejestracja:ponad 13 lat
  • Ostatnio:prawie 3 lata
  • Postów:4882
1

@Silv: input wrzuc w kolejkę fifo. Naciśniesz kilka i wrzucisz do kolejki.

1 zbierasz input
2. W pętli gdzieś masz tam rysowanie
3 po rysowaniu przetwarzasz kolejkę inputow

Freja Draco
Freja Draco
  • Rejestracja:prawie 7 lat
  • Ostatnio:ponad 3 lata
  • Postów:3394
3

Che, che. To jest przykład programu, który łatwiej pisze się w oparciu o starą dobrą pętlę niż jakieś akcje/zdarzenia.

  • W keyPressed czytaj sobie naciśnięcie klawisza i ustaw flagę naciśniętego klawisza (licz tylko ostatnio wciśnięty, żadnych buforów).
  • W pętli setInterval sprawdzasz stan klawiszy (flagę) i rysujesz kolejną klatkę ruchu.

Jak wąż porusza się w poziomie, to reaguje tyko na naciśnięcia góra/dół, a jak porusza się w pionie to tylko na prawo/lewo.
Przy starcie odpalasz go zawsze w tym samym miejscu i kierunku.


edytowany 2x, ostatnio: Freja Draco
Zobacz pozostałe 2 komentarze
Maciej Cąderek
Maciej Cąderek
Opóźnienie jest jednorazowe, co nie znaczy, że musisz je wywoływać raz. Prosty przykład pętli gry z pseudorekurencją: https://codepen.io/caderek/pen/aboNZzO?editors=0010 (sama zawartość "gry", i to że jest to canvas jest nieistotne, zasada jest ta sama)
Maciej Cąderek
Maciej Cąderek
Jeszcze chyba należy się wyjaśnienie czemu nie setInterval: w przypadku gdy gra nie zmieści się z renderowaniem klatki w tej 1/60 sekundy to problem zacznie się nawarstwiać - kolejna klatka zostanie wrzucona do kolejki zanim poprzednia zdąży sie wyrenderować, co prowadzi do wielu nieporządanych efektów - skoki tempa gry, dalsze zamulenie, itp. W przypadku setTimeout gra zawsze czeka aż poprzednia klatka będzie gotowa.
Freja Draco
Freja Draco
@Maciej Cąderek: to zależy jak się ma czas wykonywania funkcji graficznych do długości interwału. Czy wąż potrzebuje 60 klatek na sekundę dla uzyskania sensownej animacji?
Maciej Cąderek
Maciej Cąderek
Nie no jasne, pewnie w większości przypadków się "uda" - tylko po co ryzykować, jak poprawka jest w miarę prosta?
Silv
@Maciej Cąderek: dzięki! Wykorzystałbym to, bo ciekawe, gdyby nie to, że setInterval dobrze na razie działa. Gdyby były problemy z wydajnością, spróbuję.
Silv
Moderator Wiki
  • Rejestracja:ponad 10 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Warszawa
0

@fasadin: próbowałem tego. W mojej ocenie jest zbyt nieintuicyjnym, gdy naciskając jedne klawisze, wykonują się akcje dla innych.

@Freja Draco: hm... bo ja wiem, czy lepiej w oparciu o pętlę? W jakim języku mogłabyś na coś innego zamienić zdarzenie naciśnięcia klawisza?

Twoje rozwiązanie to to, co @Crazy_Rockman napisał. I wybrałem je, bo jest najlepsze z dotychczasowych nienajlepszych.


PS. Najlepsze rozwiązanie byłoby chyba w oparciu o czas, który minął od ostatniego naciśnięcia konkretnego klawisza. Nie chcę jednak go wprowadzać, bo zaśmieciłbym sobie kod nie-aż-tak-potrzebnymi obecnie warunkami i zmiennymi.


edytowany 1x, ostatnio: Silv
Freja Draco
Freja Draco
  • Rejestracja:prawie 7 lat
  • Ostatnio:ponad 3 lata
  • Postów:3394
0
Silv napisał(a):

@Freja Draco: hm... bo ja wiem, czy lepiej w oparciu o pętlę? W jakim języku mogłabyś na coś innego zamienić zdarzenie naciśnięcia klawisza?

Napisałam kiedyś Węża w Basicu. W pętli głównej programu przy każdym kolejnym kroku węża wywoływana była funkcja inkey$ czyli "sprawdź jaki klawisz został ostatnio wciśnięty" i w zależności od jego stanu program podejmował kolejne reakcje. I paradoksalnie było to prostsze niż w języku mającym reagować na konkretne zdarzenia.


edytowany 2x, ostatnio: Freja Draco
Silv
Ciekawe. :) Szkoda, że nie mam czasu, to bym sobie poczytał o tym.
Maciej Cąderek
Maciej Cąderek
  • Rejestracja:ponad 9 lat
  • Ostatnio:około 3 lata
  • Lokalizacja:Warszawa
  • Postów:1264
2
Silv napisał(a):

W jakim języku mogłabyś na coś innego zamienić zdarzenie naciśnięcia klawisza?

Np. w JavaScript ;)

Prymitywny przykład: https://codepen.io/caderek/pen/GRKZqBG?editors=0010

Nawet gdybyś w twoim przypdku chciał kolejkować klawisze (jak ktoś wcześniej napisał), to idea pozostaje taka sama - event handlery nie obsługują gry bezpośrednio - przekazują tylko informacje o klawiszach dalej, resztą zajmuje się game loop. Dzięki temu piszesz w zasadzie kod "synchroniczny" i łatwiej kontrolować co dokładnie dzieje się na ekranie.

edytowany 5x, ostatnio: Maciej Cąderek
Silv
Chodziło mi bardziej o niedziałanie na zdarzeniach w ogóle, ale OK. Lubię synchroniczność, pomyślę także nad tym, gdyby były problemy z wydajnością lub asynchronicznością.
Maciej Cąderek
Maciej Cąderek
Btw też się ostatnio wziąłem za grę, tyle, że w WebGL, także akurat jestem w temacie ;)
Silv
Ja swojej mam nadzieję nie ciągnąć... ot, coś innego niż zwykle. Skupię się po skończeniu na bracket-string-validator, Angularze (ech...) i CoyoteNET.
Silv
Hm... Zrobiłem właśnie refaktoryzację. Pierwsze uruchomienie po niej wyświetliło napis Game over i nic więcej.
Silv
Ale, no. Zapomniałem, że dodałem jeszcze nową funkcjonalność...
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)