Określanie, czy liczba jest wielokrotnością dziesięciu, czy w określonym zestawie zakresów

104

Mam kilka pętli, których potrzebuję w moim programie. Potrafię napisać pseudokod, ale nie jestem do końca pewien, jak napisać je logicznie.

Potrzebuję -

if (num is a multiple of 10) { do this }

if (num is within 11-20, 31-40, 51-60, 71-80, 91-100) { do this }
else { do this } //this part is for 1-10, 21-30, 41-50, 61-70, 81-90

To jest dla gry planszowej węże i drabiny, jeśli ma to więcej sensu w przypadku mojego pytania.

Wyobrażam sobie pierwszą instrukcję if , w której będę musiał użyć modułu. Byłoby if (num == 100%10)poprawne?

Drugi nie mam pojęcia. Mogę to napisać w ten sposób if (num > 10 && num is < 21 || etc.), ale musi być coś mądrzejszego.

user3419168
źródło
16
Ogólnie, długość dobrego kodu jest proporcjonalna do długości angielskiego opisującego, co robi. Więc kiedy w twojej „specyfikacji” jest napisane 11-20, 31-40, 51-60, 71-80, 91-100, możesz oczekiwać, że twój kod będzie zawierał również te liczby. Jeśli te liczby skądś pochodzą lub zostały wygenerowane z jakiegoś powodu, sprawdź, czy możesz zakodować przyczynę, a nie liczby.
luqui
39
@ user3419168: Kompilator nie dba o to, jak czytelny jest Twój kod; skompiluje go w ułamku sekundy. Ale dla ludzi czytających Twój kod wybory, których dokonujesz, mogą spowodować, że zostanie on zrozumiany w ciągu kilku sekund, minut, godzin lub nigdy. To pociąga za sobą koszty; ludzie otrzymują wynagrodzenie za czytanie i rozumienie kodu, więc ułatw im to. Zawsze pisz kod produkcyjny, aby zmaksymalizować czytelność i pamiętaj, że zwięzłość niekoniecznie oznacza, że ​​kod działa lepiej.
Eric Lippert
2
@AmadeusDrZaius - BARDZO RZADKO zrobiłem to samo, ale tylko dla sekcji krytycznych dla wydajności. Najściślejsza pętla, nazywana 100M razy, kwalifikuje się - stwierdzenie if w grze węży i ​​drabin nie. To, gdzie wytyczysz granicę między nimi, jest osobistym wyborem.
Floris
2
Nienawidzę tego mówić, ale mając wystarczająco dużo pracy w korporacji, z początkującymi piszącymi prawdziwy kod, musiałbym polecić brutalne wymuszanie tego. Ponieważ wtedy nowi faceci to zrozumieją i nie zepsują. smutne, ale prawdziwe - w niektórych przypadkach mądrze jest być głupim.
Richard Le Mesurier
22
To przyzwoite pytanie i nie chcę nic odejmować z plakatu, ale to nie zasługuje na ponad 500 punktów. W ten sposób kończymy z niektórymi bzdurami, które robimy, gdy ludzie z tysiącami punktów wydają się tutaj być autorytetami. (Możesz przenieść ten komentarz, jeśli należy on gdzie indziej.)
GaTechThomas

Odpowiedzi:

87

W przypadku pierwszego, aby sprawdzić, czy liczba jest wielokrotnością użycia:

if (num % 10 == 0) // It's divisible by 10

W przypadku drugiego:

if(((num - 1) / 10) % 2 == 1 && num <= 100)

Ale to dość gęste i może być lepiej, jeśli po prostu wymień opcje bezpośrednio.


Teraz, gdy masz już lepsze wyobrażenie o tym, co robisz, drugą napisałbym jako:

   int getRow(int num) {
      return (num - 1) / 10;
   }

   if (getRow(num) % 2 == 0) {
   }

To ta sama logika, ale używając funkcji, uzyskujemy jaśniejsze pojęcie o tym, co ona oznacza.

Winston Ewert
źródło
79
if((num - 1) / 10) % 2 == 1 && num < 100)- Płakałbym, gdybym to zobaczył.
Daniel Kamil Kozar
32
@DanielKamilKozar, tak jak powinieneś.
Winston Ewert
2
@ user3419168, sam z siebie pozostawia do zastanowienia się, co to znaczy na świecie. Nie daje żadnych wskazówek, co na świecie próbuje zrobić. Dlatego w edycji pokazałem wersję, która dzieli logikę na funkcję, dzięki czemu jaśniej widać, co faktycznie robią obliczenia.
Winston Ewert
3
Rozsądne może być również twierdzenie num >= 11jako (1), że dolna granica jest zakazana, a (2) %na liczbie ujemnej zwraca również liczbę ujemną. (Muszę przyznać, że używanie & 1tutaj jest "bezpieczniejsze", ale wymaga też dodatkowej wiedzy.)
usr2564301 Kwietnia
2
+1 dla Edycji, dostaje się do przyczyny listy zakresów i przedstawia ją w czytelny sposób. IMO, jednym krokiem byłoby getRow(num) % 2 == 0zawarcie funkcji, aby było jasne, jaki jest zamiar. bool inEvenRow(int num){ return getRow(num) % 2 ==0;}
Pan Mindor
40

if (num to wielokrotność 10) {zrób to}

if (num % 10 == 0) {
  // Do something
}

if (num należy do 11-20, 31-40, 51-60, 71-80, 91-100) {zrób to}

Sztuczka polega na tym, aby szukać pewnego rodzaju podobieństwa między zakresami. Oczywiście zawsze możesz użyć metody „brutalnej siły”:

if ((num > 10 && num <= 20) ||
    (num > 30 && num <= 40) ||
    (num > 50 && num <= 60) ||
    (num > 70 && num <= 80) ||
    (num > 90 && num <= 100)) {
  // Do something
}

Ale możesz zauważyć, że jeśli odejmiesz 1od num, będziesz mieć zakresy:

10-19, 30-39, 50-59, 70-79, 90-99

Innymi słowy, wszystkie dwucyfrowe liczby, których pierwsza cyfra jest nieparzysta. Następnie musisz wymyślić formułę, która to wyraża. Możesz otrzymać pierwszą cyfrę, dzieląc przez 10, i możesz sprawdzić, czy jest nieparzysta, sprawdzając resztę 1, gdy dzielisz przez 2. Składając to wszystko razem:

if ((num > 0) && (num <= 100) && (((num - 1) / 10) % 2 == 1)) {
  // Do something
}

Biorąc pod uwagę kompromis między dłuższym, ale możliwym do utrzymania kodem, a krótszym „inteligentnym” kodem, za każdym razem wybrałbym dłuższy i wyraźniejszy. Przynajmniej, jeśli starasz się być sprytny, dołącz komentarz, który dokładnie wyjaśnia, co próbujesz osiągnąć.

Pomaga założyć, że następny programista, który będzie pracował nad kodem, jest uzbrojony i wie, gdzie mieszkasz. :-)

Adam Liss
źródło
7
Nadal bym wybrał sprytny kod, ale przekształciłbym go w kod, który można by utrzymać, wyodrębniając funkcje. Byłoby równie czytelne, gdyby ten ostatni bit powiedział && isTensDigitOdd(num), być może z komentarzem przed definicją funkcji wyjaśniającym, co robi. Jeśli taki wzorzec istnieje, komentarz wyjaśniający uzasadnienie wzorca jest pouczający w kwestii konserwacji imo.
chris
3
Chris, to świetna strategia, gdy „spryt” ma wyraźną przewagę: znacznie krótszy kod (co oznacza mniejsze prawdopodobieństwo pomyłki, zwłaszcza jeśli się zmienia) lub dużą poprawę wydajności. Prawie zawsze istnieje kompromis między zwięzłością, przejrzystością i wydajnością, a znalezienie dobrego kompromisu to świetna umiejętność do rozwinięcia. (Zobacz stackoverflow.com/a/2151844/29157 dla parsknięcia.)
Adam Liss
1
To znacznie lepsze podejście. O wiele łatwiejszy do zrozumienia niż „sprytny kod”, a różnica w wydajności jest prawdopodobnie nieistotna.
user1477388
@AdamLiss, Tak, moja opinia ma niewielką wartość, ponieważ nie mam wystarczającego doświadczenia, aby zobaczyć konsekwencje tych decyzji. Jestem pewien, że niedługo to zrobię i na pewno uzyskam drugą opinię, jeśli zajdzie taka potrzeba.
chris
1
Nie sprzedawaj się krótko. Twoje instynkty są bardzo rozsądne i wydajesz się chętny do dalszej nauki. Każda opinia jest cenna, jeśli ma ku temu dobry powód ... a czasem nawet jeśli jej nie ma. Założę się, że zajdziesz daleko.
Adam Liss
30

Jeśli używasz GCC lub dowolnego kompilatora obsługującego zakresy wielkości liter, możesz to zrobić, ale Twój kod nie będzie przenośny .

switch(num)
{
case 11 ... 20:
case 31 ... 40:
case 51 ... 60:
case 71 ... 80:
case 91 ... 100:
    // Do something
    break;
default:
    // Do something else
    break;
}
Bryan Chen
źródło
1
Czy możesz mi powiedzieć, dlaczego ten kod nie jest przenośny?
M Sharath Hegde
8
@MSharathHegde, ponieważ wymaga rozszerzenia GCC, które nie jest częścią standardu i niektóre kompilatory go nie obsługują
Bryan Chen
5
To jest właściwa odpowiedź, ponieważ od razu widać, jaki jest zamiar. Wszystkie te „sprytne” odpowiedzi z modulo są koszmarem konserwacji, nawet z komentarzami.
smirkingman
@smirkingman Rzeczywiście, to właśnie powiedziałem w moim komentarzu do głównego pytania. Wystarczy trochę doświadczenia nowych programistów w pracy w korporacji, aby zdać sobie sprawę, że oczywista droga jest często lepsza niż droga inteligentnego ninja.
Richard Le Mesurier
15

To jest bardziej dla przyszłych gości niż początkujących. Aby uzyskać bardziej ogólne rozwiązanie podobne do algorytmu, możesz wziąć listę wartości początkowych i końcowych i sprawdzić, czy przekazana wartość znajduje się w jednej z nich:

template<typename It, typename Elem>
bool in_any_interval(It first, It last, const Elem &val) {
    return std::any_of(first, last, [&val](const auto &p) {
        return p.first <= val && val <= p.second;
    });
}

Dla uproszczenia użyłem polimorficznej lambda (C ++ 14) zamiast jawnego pairargumentu. Powinno to również prawdopodobnie trzymać się używania <i ==być spójne ze standardowymi algorytmami, ale działa tak długo, jak Elemzostało <=to dla niego zdefiniowane. W każdym razie można go używać w następujący sposób:

std::pair<int, int> intervals[]{
    {11, 20}, {31, 40}, {51, 60}, {71, 80}, {91, 100}
};

const int num = 15;
std::cout << in_any_interval(std::begin(intervals), std::end(intervals), num);

Jest żywy przykład tutaj .

chris
źródło
Zgrabne rozwiązanie. Prawdopodobnie użyłbym pojedynczej tablicy, ponieważ możesz sformatować ją za pomocą 2 liczb w wierszu, aby przedstawić pary.
Kevin Lam
@ HunterGuy2, bardzo dobra uwaga. Właściwie zamierzam to zmienić, aby działał na parach, ponieważ z jakiegoś powodu myślałem tylko o iteratorach zip.
chris
Naprawdę fajne podejście do stylu! Kocham to!
higuaro
5

Pierwsza jest łatwa. Wystarczy zastosować operator modulo do wartości num:

if ( ( num % 10 ) == 0)

Ponieważ C ++ ocenia każdą liczbę inną niż 0 jako prawdę, możesz również napisać:

if ( ! ( num % 10 ) )  // Does not have a residue when divided by 10

Po drugie, myślę, że jest to bardziej przejrzyste do zrozumienia:

Wzór powtarza się co 20, więc możesz obliczyć modulo 20. Wszystkie elementy, które chcesz, będą w rzędzie, z wyjątkiem tych, które są podzielne przez 20.

Aby je również uzyskać, po prostu użyj num-1 lub lepiej num + 19, aby uniknąć zajmowania się liczbami ujemnymi.

if ( ( ( num + 19 ) % 20 ) > 9 )

Zakłada się, że wzór będzie się powtarzał w nieskończoność, więc dla 111-120 będzie obowiązywał ponownie i tak dalej. W przeciwnym razie musisz ograniczyć liczby do 100:

if ( ( ( ( num + 19 ) % 20 ) > 9 ) && ( num <= 100 ) )
kasimir
źródło
5

Dzięki kilku dobrym komentarzom w kodzie można go napisać dość zwięźle i czytelnie.

// Check if it's a multiple of 10
if (num % 10 == 0) { ... }

// Check for whether tens digit is zero or even (1-10, 21-30, ...)
if ((num / 10) % 2 == 0) { ... }
else { ... }
La-comadreja
źródło
2
Pierwszy komentarz jest niepotrzebny. Każdy programista z niewielkim doświadczeniem będzie wiedział, że num % 10 == 0to to samo, co numjest wielokrotnością liczby 10.
Justin
7
tak, ale początkujący też czytają tę stronę. Normalnie nie użyłbym tego komentarza w moim własnym kodzie, ale sprawia, że ​​odpowiedź jest jaśniejsza dla początkujących, którzy skorzystaliby na tym pytaniu dla początkujących.
La-comadreja
2
Proszę, nigdy tego nie rób. W rzeczywistości zmniejsza czytelność, spowalniając czytelnika i zmuszając go do przeczytania wszystkiego dwa razy. Każdy programista, który tego nie rozumie, if (num % 10 == 0)oznacza to samo, co // Check if it's a multiple of 10nie powinien zajmować się Twoim kodem. To dobrze znany anty-wzór.
Dawood ibn Kareem
1
@DavidWallace patrz komentarz powyżej. Nie możemy zagwarantować, że czytelnicy tego postu będą znali ten anty-wzór.
La-comadreja
1
Nie, mam na myśli to, że komentowanie każdej linii, aby powiedzieć, co robi, jest anty-wzorcem. Nie mam na myśli tego, że używanie %jest anty-wzorcem; oczywiście tak nie jest. Naprawdę, zakładając, że wielu czytelników tego posta będzie początkującymi, nauczenie ich tego stylu pisania komentarzy ma negatywny wpływ na ich rozwój jako programistów.
Dawood ibn Kareem
4

W zasadzie sam wyjaśniłeś odpowiedź, ale na wszelki wypadek oto kod.

if((x % 10) == 0) {
  // Do this
}
if((x > 10 && x < 21) || (x > 30 && x < 41) || (x > 50 && x < 61) || (x > 70 && x < 81) || (x > 90 && x < 101)) {
  // Do this
}
Henry Harris
źródło
2
Popraw x < 41 x > 50i umieść nawiasy.
101010
1
@ 40two, technicznie rzecz biorąc, operator&&ma wyższy priorytet niż operator||, więc jest w porządku, ale jestem prawie pewien, że GCC i tak ostrzega przed tym.
chris
18
Rozważ reprezentowanie nierówności raczej 10 < x < 21jako 10 < x && x < 21niż x > 10 && x < 21. Łatwiej jest odczytać nierówność, gdy jest w tej samej kolejności, w jakiej zapisujesz ją matematycznie.
Eric Lippert
5
Ten kod jest dość nieczytelny i niewiele mówi o rzeczywistej logice. Nie podoba mi się ta odpowiedź.
Dariusz
3
Odrzucam to, ponieważ odpowiedziałeś dokładnie, co zrobił OP.
Bruno Ferreira
3

Możesz się nad tym zastanawiać.

if (x % 10)
{
   .. code for 1..9 ..
} else
{
   .. code for 0, 10, 20 etc.
}

Pierwsza linia if (x % 10)działa, ponieważ (a), to wartość, która jest wielokrotnością 10 oblicza jako „0”, inne liczby spowodować ich remainer, (b) wartość 0 w sposób ifjest uważany falsekażda inna wartość true.

Edytować:

Aby przełączać się tam iz powrotem w latach dwudziestych, użyj tej samej sztuczki. Tym razem kluczowa liczba to 10:

if (((x-1)/10) & 1)
{
  .. code for 10, 30, ..
} else
{
   .. code for 20, 40, etc.
}

x/10zwraca dowolną liczbę od 0 do 9 jako 0, od 10 do 19 jako 1i tak dalej. Testowanie parzyste lub nieparzyste - & 1- mówi, czy jest parzyste czy nieparzyste. Ponieważ Twoje zakresy to w rzeczywistości „11 do 20”, przed testowaniem odejmij 1.

usr2564301
źródło
1

Apel o czytelność

Chociaż masz już kilka dobrych odpowiedzi, chciałbym polecić technikę programowania, która sprawi, że Twój kod będzie bardziej czytelny dla jakiegoś przyszłego czytelnika - możesz być za sześć miesięcy, kolega poprosił o dokonanie przeglądu kodu, twój następca ... .

Ma to na celu zawinięcie wszelkich „sprytnych” instrukcji w funkcję, która dokładnie (wraz ze swoją nazwą) pokazuje, co robi. Chociaż istnieje niewielki wpływ na wydajność (wynikający z „narzutu wywołań funkcji”), jest to naprawdę pomijalne w takiej sytuacji w grze.

Po drodze możesz oczyścić swoje dane wejściowe - na przykład przetestować pod kątem „niedozwolonych” wartości. Tak więc możesz skończyć z takim kodem - zobacz, o ile jest bardziej czytelny? „Funkcje pomocnicze” można gdzieś ukryć (nie muszą znajdować się w głównym module: z ich nazwy jasno wynika, co robią):

#include <stdio.h>

enum {NO, YES, WINNER};
enum {OUT_OF_RANGE=-1, ODD, EVEN};

int notInRange(int square) {
  return(square < 1 || square > 100)?YES:NO;
}

int isEndOfRow(int square) {
  if (notInRange(square)) return OUT_OF_RANGE;
  if (square == 100) return WINNER; // I am making this up...
  return (square % 10 == 0)? YES:NO;
}

int rowType(unsigned int square) {
  // return 1 if square is in odd row (going to the right)
  // and 0 if square is in even row (going to the left)
  if (notInRange(square)) return OUT_OF_RANGE; // trap this error
  int rowNum = (square - 1) / 10;
  return (rowNum % 2 == 0) ? ODD:EVEN; // return 0 (ODD) for 1-10, 21-30 etc.
                                       // and 1 (EVEN) for 11-20, 31-40, ...
}

int main(void) {
  int a = 12;
  int rt;
  rt = rowType(a); // this replaces your obscure if statement

  // and here is how you handle the possible return values:
  switch(rt) {
  case ODD:
    printf("It is an odd row\n");
    break;
  case EVEN:
    printf("It is an even row\n");
    break;
  case OUT_OF_RANGE:
    printf("It is out of range\n");
    break;
  default:
    printf("Unexpected return value from rowType!\n");
  }

  if(isEndOfRow(10)==YES) printf("10 is at the end of a row\n");
  if(isEndOfRow(100)==WINNER) printf("We have a winner!\n");
}
Floris
źródło
3
Czy nie próbuje posunąć się za daleko za pomocą YESi NO?
rmobis
@Raphael_ - może tak być: właśnie pokazywałem „na przykład”. Oczywiście wiele osób używa prawdy / fałszu. Ale ja nigdy nie pamiętam (bo różne języki wykorzystują różne konwencje): jest to TRUE, Truealbo true? A jakie pliki nagłówkowe musiałbym umieścić w zwykłym C? Więc skręciłem własne. Ciekawe, czy to właśnie spotkało się z negatywną opinią ...
Floris
1

Na pierwszy:

if (x % 10 == 0)

będzie dotyczyć:

10, 20, 30, .. 100 .. 1000 ...

W przypadku drugiego:

if (((x-1) / 10) % 2 == 1)

będzie ubiegać się o:

11-20, 31-40, 51-60, ..

Zasadniczo najpierw robimy, x-1aby uzyskać:

10-19, 30-39, 50-59, ..

Następnie dzielimy je, 10aby otrzymać:

1, 3, 5, ..

Więc sprawdzamy, czy ten wynik jest dziwny.

Khaled.K
źródło
1

Możesz spróbować następujących rzeczy:

// Multiple of 10
if ((num % 10) == 0)
{
   // Do something
}
else if (((num / 10) % 2) != 0)
{
    // 11-20, 31-40, 51-60, 71-80, 91-100
}
 else
{
    // Other case
}
ShalakaV
źródło
W pytaniu OP sprawdzenie wielokrotności 10 nie jest związane ze sprawdzeniem zakresu, aw przypadku sprawdzenia zakresu 20 musi znajdować się w tym samym zakresie 11, z twoim kodem ((20/10)% 2) -> ( 2% 2) -> 0
Serpiton
0

Wiem, że na to pytanie jest tak wiele odpowiedzi, ale moje i tak wrzucę tutaj ...

Zaczerpnięte z książki Steve McConnell's Code Complete , 2. wydanie: „Tabele dostępu po schodach:

Jeszcze innym rodzajem dostępu do stołu jest metoda schodkowa. Ta metoda dostępu nie jest tak bezpośrednia jak struktura indeksu, ale nie marnuje tak dużo miejsca na dane. Ogólna idea struktur schodkowych, zilustrowana na rysunku 18-5, polega na tym, że wpisy w tabeli są prawidłowe dla zakresów danych, a nie dla odrębnych punktów danych.

Tutaj wprowadź opis obrazu

Rysunek 18-5 Podejście schodkowe kategoryzuje każde wejście poprzez określenie poziomu, na którym uderza w „klatkę schodową”. „Krok”, który uderza, określa jego kategorię.

Na przykład, jeśli piszesz program oceniania, zakres pozycji „B” może wynosić od 75% do 90%. Oto szereg ocen, które pewnego dnia będziesz musiał zaprogramować:

Tutaj wprowadź opis obrazu

Aby użyć metody schodkowej, należy umieścić górny koniec każdego zakresu w tabeli, a następnie napisać pętlę, aby porównać wynik z górnym końcem każdego zakresu. Kiedy znajdziesz punkt, w którym wynik po raz pierwszy przekracza górną granicę zakresu, wiesz, jaka jest ocena. W przypadku techniki schodkowej należy uważać, aby prawidłowo obsługiwać punkty końcowe zakresów. Oto kod w języku Visual Basic, który przypisuje oceny grupie uczniów na podstawie tego przykładu:

Tutaj wprowadź opis obrazu

Chociaż jest to prosty przykład, można go łatwo uogólnić, aby uwzględnić wielu uczniów, wiele schematów oceniania (na przykład różne oceny dla różnych poziomów punktów za różne zadania) i zmiany w schemacie oceniania ”.

Code Complete , 2. wydanie, strony 426 - 428 (rozdział 18).

lauCosma
źródło
dlaczego uważasz, że to złe pytanie? tylko dlatego, że nie
podałem
0

Jak zauważyli inni, bardziej zwięzłe warunki nie przyspieszą kompilacji ani wykonywania, a także niekoniecznie poprawią czytelność.

Może pomóc w uczynieniu programu bardziej elastycznym, na wypadek gdybyś później zdecydował, że chcesz wersję gry dla maluchów na planszy 6 x 6 lub wersję zaawansowaną (którą możesz grać całą noc) na planszy 40 x 50 .

Więc zakodowałbym to w następujący sposób:

// What is the size of the game board?
#define ROWS            10
#define COLUMNS         10

// The numbers of the squares go from 1 (bottom-left) to (ROWS * COLUMNS)
// (top-left if ROWS is even, or top-right if ROWS is odd)
#define firstSquare     1
#define lastSquare      (ROWS * COLUMNS)
// We haven't started until we roll the die and move onto the first square,
// so there is an imaginary 'square zero'
#define notStarted(num) (num == 0)
// and we only win when we land exactly on the last square
#define finished(num)   (num == lastSquare)
#define overShot(num)   (num > lastSquare)

// We will number our rows from 1 to ROWS, and our columns from 1 to COLUMNS
// (apologies to C fanatics who believe the world should be zero-based, which would
//  have simplified these expressions)
#define getRow(num)   (((num - 1) / COLUMNS) + 1)
#define getCol(num)   (((num - 1) % COLUMNS) + 1)

// What direction are we moving in?
// On rows 1, 3, 5, etc. we go from left to right
#define isLeftToRightRow(num)    ((getRow(num) % 2) == 1)
// On rows 2, 4, 6, etc. we go from right to left
#define isRightToLeftRow(num)    ((getRow(num) % 2) == 0)

// Are we on the last square in the row?
#define isLastInRow(num)    (getCol(num) == COLUMNS)

// And finally we can get onto the code

if (notStarted(mySquare))
{
  // Some code for when we haven't got our piece on the board yet
}
else
{
  if (isLastInRow(mySquare))
  {
    // Some code for when we're on the last square in a row
  }


  if (isRightToLeftRow(mySquare))
  {
    // Some code for when we're travelling from right to left
  }
  else
  {
    // Some code for when we're travelling from left to right
  }
}

Tak, jest rozwlekły, ale dokładnie wyjaśnia, co dzieje się na planszy.

Gdybym tworzył tę grę do wyświetlania na telefonie lub tablecie, zrobiłbym zmienne ROWS i COLUMNS zamiast stałych, aby można je było ustawić dynamicznie (na początku gry), aby dopasować rozmiar ekranu i orientację.

Pozwoliłbym również na zmianę orientacji ekranu w dowolnym momencie, w trakcie gry - wystarczy zmienić wartości ROWS i COLUMNS, pozostawiając wszystko inne (bieżący numer kwadratu, na którym jest każdy gracz, i kwadraty początku / końca wszystkich węży i ​​drabin) bez zmian. Następnie musisz „tylko” ładnie narysować planszę i napisać kod do swoich animacji (zakładam, że taki był cel twoich ifwypowiedzi) ...

Laurence Renshaw
źródło
Głosowałem również pozytywnie na odpowiedź Florisa - inny styl osiągania podobnego wyniku, którego nie widziałem przed napisaniem odpowiedzi
Laurence Renshaw
2
powinieneś użyć funkcji inline zamiast#define
Bryan Chen
Dobrą praktyką #definejest umieszczanie argumentów w nawiasach w miejscach, w których pojawiają się one w rozwinięciu, przy użyciu instrukcji funkcjonalnych . Więc zamiast tego #define finished(num) (num == lastSquare)powinieneś pisać #define finished(num) ((num) == lastSquare). Powodem jest to, że jeśli użyjesz takiej instrukcji z wyrażeniem zawierającym operator o odpowiednio niskim priorytecie, nie otrzymasz oczekiwanej odpowiedzi. W tym przypadku, jeśli nie użyjesz dodatkowych nawiasów, to finished(a & b)rozwinie się, (a & b == lastSquare)co prawie na pewno nie jest tym, czego chcesz.
Dawood ibn Kareem