Bestimmen, ob eine Zahl entweder ein Vielfaches von zehn oder innerhalb eines bestimmten Satzes von Bereichen ist

103

Ich habe ein paar Schleifen, die ich in meinem Programm brauche. Ich kann den Pseudocode ausschreiben, bin mir aber nicht ganz sicher, wie ich sie logisch schreiben soll.

Ich brauche -

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

Dies ist für ein Brettspiel mit Schlangen und Leitern, wenn es für meine Frage sinnvoller ist.

Ich stelle mir die erste if-Anweisung vor, die ich verwenden muss, um den Modul zu verwenden if (num == 100%10) das richtig?

Den zweiten habe ich keine Ahnung. Ich kann es if (num > 10 && num is < 21 || etc)so ausschreiben, aber es muss etwas Klügeres geben.

user3419168
quelle
16
Im Allgemeinen ist die Länge eines guten Codes proportional zur Länge des Englischen, das beschreibt, was es tut. Wenn Ihre "Spezifikation" also 11-20, 31-40, 51-60, 71-80, 91-100 lautet, können Sie erwarten, dass Ihr Code auch diese Nummern erwähnt. Wenn diese Zahlen von irgendwoher kommen oder aus irgendeinem Grund generiert wurden, prüfen Sie, ob Sie den Grund und nicht die Zahlen codieren können.
Luqui
39
@ user3419168: Der Compiler kümmert sich nicht darum, wie lesbar Ihr Code ist. es wird es in Sekundenbruchteilen kompilieren. Aber für die Menschen, die Ihren Code lesen, können die Entscheidungen, die Sie treffen, dazu führen, dass er in Sekunden, Minuten, Stunden oder nie verstanden wird. Dies verursacht Kosten; Die Leute werden dafür bezahlt, Code zu lesen und zu verstehen, also machen Sie es ihnen einfach. Schreiben Sie immer Produktionscode, um die Lesbarkeit zu maximieren, und denken Sie daran, dass die Genauigkeit nicht unbedingt zu einer besseren Leistung des Codes führt.
Eric Lippert
2
@AmadeusDrZaius - Ich habe SEHR SELTEN das Gleiche getan, aber nur für leistungskritische Abschnitte. Die engste Schleife, die 100 Millionen Mal aufgerufen wird, qualifiziert sich - die if-Anweisung in einem Spiel mit Schlangen und Leitern nicht. Wo Sie die Grenze zwischen ihnen ziehen, ist eine persönliche Entscheidung.
Floris
2
Ich hasse es, es zu sagen, aber nachdem ich genug Unternehmensarbeit geleistet habe und Anfänger echten Code geschrieben haben, muss ich empfehlen, ihn brutal zu erzwingen. Denn dann werden die neuen Leute es verstehen und es nicht brechen. traurig, aber wahr - in einigen Fällen ist es klug, dumm zu sein.
Richard Le Mesurier
22
Dies ist eine anständige Frage, und ich möchte dem Poster überhaupt nichts wegnehmen, aber dies verdient nicht mehr als 500 Punkte. Auf diese Weise erhalten wir einen Teil des Unsinns, den wir mit Menschen machen, bei denen Tausende von Punkten hier als Autoritäten erscheinen. (Fühlen Sie sich frei, diesen Kommentar zu verschieben, wenn er woanders
hingehört

Antworten:

86

Überprüfen Sie beim ersten Mal, ob eine Zahl ein Vielfaches der Verwendung ist:

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

Für den zweiten:

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

Aber das ist ziemlich dicht, und Sie sollten die Optionen besser explizit auflisten.


Nachdem Sie eine bessere Vorstellung davon haben, was Sie tun, würde ich die zweite schreiben als:

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

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

Es ist die gleiche Logik, aber durch die Verwendung der Funktion erhalten wir eine klarere Vorstellung davon, was dies bedeutet.

Winston Ewert
quelle
79
if((num - 1) / 10) % 2 == 1 && num < 100)- Ich würde weinen, wenn ich das sehen würde.
Daniel Kamil Kozar
32
@ DanielKamilKozar, wie du solltest.
Winston Ewert
2
@ user3419168, allein lässt es einen fragen, was in aller Welt es bedeutet. Es gibt keine Hinweise darauf, was in aller Welt es versucht zu tun. Deshalb habe ich in der Bearbeitung eine Version gezeigt, die die Logik in eine Funktion aufteilt, die klarer macht, was die Berechnungen tatsächlich tun.
Winston Ewert
3
Es kann ratsam sein, auch zu behaupten num >= 11, dass (1) die Untergrenze verboten ist und (2) %bei einer negativen Zahl ebenfalls eine negative Zahl zurückgegeben wird. (Ich muss zugeben, dass die Verwendung & 1hier "sicherer" ist, setzt aber auch zusätzliches Wissen voraus.)
usr2564301
2
+1 für die Bearbeitung, es wird in das Warum der Liste der Bereiche aufgenommen und es ist lesbar dargestellt. IMO, ein Schritt nach oben wäre, das getRow(num) % 2 == 0in eine Funktion zu wickeln , um kristallklar zu machen, was die Absicht ist. bool inEvenRow(int num){ return getRow(num) % 2 ==0;}
Mr. Mindor
40

if (num ist ein Vielfaches von 10) {mach das}

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

if (num liegt innerhalb von 11-20, 31-40, 51-60, 71-80, 91-100) {do this}

Der Trick dabei ist, nach einer Art Gemeinsamkeit zwischen den Bereichen zu suchen. Natürlich können Sie immer die "Brute Force" -Methode verwenden:

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

Aber Sie können feststellen , dass, wenn Sie subtrahieren 1aus num, Sie werden die Bereiche haben:

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

Mit anderen Worten, alle zweistelligen Zahlen, deren erste Ziffer ungerade ist. Als nächstes müssen Sie eine Formel entwickeln, die dies ausdrückt. Sie können die erste Ziffer erhalten, indem Sie durch 10 teilen, und Sie können testen, ob sie ungerade ist, indem Sie beim Teilen durch 2 nach einem Rest von 1 suchen. Alles zusammen:

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

Angesichts des Kompromisses zwischen längerem, aber wartbarem Code und kürzerem "cleverem" Code würde ich jedes Mal länger und klarer wählen. Wenn Sie versuchen, klug zu sein, fügen Sie bitte einen Kommentar hinzu, der genau erklärt, was Sie erreichen möchten.

Es ist hilfreich anzunehmen, dass der nächste Entwickler, der an dem Code arbeitet, scharf ist und weiß, wo Sie leben. :-)

Adam Liss
quelle
7
Ich würde mich immer noch für den cleveren Code entscheiden, ihn aber durch Extrahieren von Funktionen in wartbaren Code verwandeln. Es wäre genauso lesbar, wenn das letzte Bit gesagt würde && isTensDigitOdd(num), vielleicht mit einem Kommentar vor der Funktionsdefinition, der erklärt, was es tut. Wenn ein solches Muster existiert, ist ein Kommentar, der die Gründe für das Muster erklärt, für die Wartbarkeit imo aufschlussreich.
Chris
3
Chris, das ist eine großartige Strategie, wenn die "Klugheit" einen klaren Vorteil hat: viel kürzerer Code (was weniger Wahrscheinlichkeit eines Tippfehlers bedeutet, insbesondere wenn er sich ändert) oder eine große Verbesserung der Effizienz. Es gibt fast immer einen Kompromiss zwischen Kürze, Klarheit und Effizienz, und es ist eine große Fähigkeit, einen guten Kompromiss zu finden. (Siehe stackoverflow.com/a/2151844/29157 für ein Kichern.)
Adam Liss
1
Dies ist ein viel besserer Ansatz. So viel einfacher zu verstehen als der "clevere Code" und der Leistungsunterschied ist wahrscheinlich vernachlässigbar.
user1477388
@AdamLiss, Ja, meine Meinung ist von geringem Wert, da ich nicht genug Erfahrung habe, um die Auswirkungen dieser Entscheidungen zu sehen. Ich bin mir sicher, dass ich es bald tun werde, und ich werde sicher eine zweite Meinung einholen, wenn nötig.
Chris
1
Verkaufen Sie sich nicht zu kurz. Ihre Instinkte sind sehr vernünftig und Sie scheinen bestrebt zu sein, weiter zu lernen. Jede Meinung ist wertvoll, wenn es einen guten Grund dafür gibt ... und manchmal auch, wenn es keinen gibt. Ich würde Geld wetten, dass Sie weit gehen werden.
Adam Liss
30

Wenn Sie GCC oder einen anderen Compiler verwenden, der dies unterstützt Fall Bereiche können Sie dies tun, aber Ihr Code nicht tragbar sein .

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
quelle
1
Können Sie mir bitte sagen, warum dieser Code nicht portabel ist?
M Sharath Hegde
8
@ MSharathHegde, weil es GCC-Erweiterung erfordert, die nicht Teil des Standards ist und einige Compiler es nicht unterstützen
Bryan Chen
5
Dies ist die richtige Antwort, denn es ist sofort ersichtlich, was die Absicht ist. Alle diese "cleveren" Antworten mit Modulo sind selbst mit Kommentaren ein Alptraum für die Wartung.
Grinsender
@smirkingman Genau das habe ich in meinem Kommentar zur Hauptfrage gesagt. Es braucht nur einige Erfahrung mit neuen Programmierern in einem Unternehmensjob, um zu erkennen, dass der offensichtliche Weg oft viel besser ist als der Smart-Ninja-Weg.
Richard Le Mesurier
15

Dies ist für zukünftige Besucher mehr als für Anfänger. Für eine allgemeinere, algorithmische Lösung können Sie eine Liste von Start- und Endwerten erstellen und prüfen, ob sich ein übergebener Wert in einem von ihnen befindet:

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;
    });
}

Der Einfachheit halber habe ich anstelle eines expliziten pairArguments ein polymorphes Lambda (C ++ 14) verwendet . Dies sollte wahrscheinlich auch bei der Verwendung bleiben <und ==mit den Standardalgorithmen übereinstimmen, aber es funktioniert so, solange Elemes dafür <=definiert wurde. Wie auch immer, es kann so verwendet werden:

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);

Es gibt ein anschauliches Beispiel hier .

chris
quelle
Ordentliche Lösung. Ich hätte wahrscheinlich ein einzelnes Array verwendet, da Sie es mit 2 Zahlen pro Zeile formatieren können, um Paare darzustellen.
Kevin Lam
@ HunterGuy2, sehr guter Punkt. Ich werde es tatsächlich ändern, um paarweise zu arbeiten, weil ich aus irgendeinem Grund nur an Zip-Iteratoren gedacht habe.
Chris
Wirklich schöner stl Ansatz! Liebe es!
Higuaro
5

Der erste ist einfach. Sie müssen nur den Modulo-Operator auf Ihren num-Wert anwenden:

if ( ( num % 10 ) == 0)

Da C ++ jede Zahl, die nicht 0 ist, als wahr auswertet, können Sie auch schreiben:

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

Für den zweiten denke ich, dass dies sauberer zu verstehen ist:

Das Muster wird alle 20 wiederholt, sodass Sie Modulo 20 berechnen können. Alle gewünschten Elemente befinden sich in einer Reihe, mit Ausnahme derjenigen, die durch 20 teilbar sind.

Um diese auch zu erhalten, verwenden Sie einfach num-1 oder besser num + 19, um den Umgang mit negativen Zahlen zu vermeiden.

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

Dies setzt voraus, dass sich das Muster für immer wiederholt, sodass es für 111-120 erneut gilt und so weiter. Andernfalls müssen Sie die Anzahl auf 100 beschränken:

if ( ( ( ( num + 19 ) % 20 ) > 9 ) && ( num <= 100 ) )
Kasimir
quelle
5

Mit ein paar guten Kommentaren im Code kann es sehr präzise und lesbar geschrieben werden.

// 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
quelle
2
Der erste Kommentar ist nicht erforderlich. Jeder Programmierer mit ein wenig Erfahrung wird wissen, dass dies num % 10 == 0dasselbe ist wie numein Vielfaches von 10.
Justin
7
Ja, aber Anfänger lesen diese Seite auch. Normalerweise würde ich diesen Kommentar nicht in meinem eigenen Code verwenden, aber er macht die Antwort für Anfänger klarer, die von dieser Anfängerfrage profitieren würden.
La-comadreja
2
Bitte tu das niemals. Es verringert tatsächlich die Lesbarkeit, indem es den Leser verlangsamt und ihn zwingt, alles zweimal zu lesen. Jeder Programmierer, der das nicht versteht, if (num % 10 == 0)bedeutet dasselbe, // Check if it's a multiple of 10sollte Ihren Code nicht pflegen . Dies ist ein bekanntes Anti-Muster.
Dawood ibn Kareem
1
@ DavidWallace siehe Kommentar oben. Wir können nicht garantieren, dass die Leser dieses Beitrags dieses Anti-Muster kennen.
La-Comadreja
1
Nein, ich meine, dass es ein Anti-Muster ist, jede Zeile zu kommentieren, um zu sagen, was sie tut. Ich meine nicht, dass das Verwenden %ein Anti-Muster ist; offensichtlich ist es nicht. Unter der Annahme, dass viele der Leser dieses Beitrags Anfänger sein werden, trägt das Unterrichten dieser Art des Schreibens von Kommentaren einen negativen Beitrag zu ihrer Entwicklung als Programmierer bei.
Dawood ibn Kareem
4

Sie haben die Antwort im Grunde selbst erklärt, aber hier ist der Code für alle Fälle.

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
quelle
2
Korrigieren x < 41 x > 50und setzen Sie Klammern.
101010
1
@ 40two hat technisch gesehen operator&&eine höhere Priorität als operator||, also ist es in Ordnung, aber ich bin mir ziemlich sicher, dass GCC trotzdem davor warnt.
Chris
18
Betrachten Sie die Darstellung der Ungleichung 10 < x < 21als 10 < x && x < 21eher als x > 10 && x < 21. Es ist einfacher, die Ungleichung zu lesen, wenn sie in derselben Reihenfolge vorliegt, in der Sie sie mathematisch schreiben würden.
Eric Lippert
5
Dieser Code ist ziemlich unlesbar und sagt wenig über die tatsächliche Logik aus. Ich mag diese Antwort nicht.
Dariusz
3
Ich stimme dem zu, weil Sie genau geantwortet haben, was das OP getan hat.
Bruno Ferreira
3

Sie könnten dies überdenken.

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

Die erste Zeile if (x % 10)funktioniert, weil (a) ein Wert, der ein Vielfaches von 10 ist, als '0' berechnet wird, andere Zahlen zu ihrem Rest führen, (b) ein Wert von 0 in a ifberücksichtigt wird false, jeder andere Wert ist true.

Bearbeiten:

Verwenden Sie den gleichen Trick, um in den zwanziger Jahren hin und her zu wechseln. Diesmal lautet die zentrale Nummer 10:

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

x/10Gibt eine beliebige Zahl von 0 bis 9 als 0, 10 bis 19 als 1usw. zurück. & 1Wenn Sie auf gerade oder ungerade testen, erfahren Sie, ob es gerade oder ungerade ist. Da Ihre Bereiche tatsächlich "11 bis 20" sind, subtrahieren Sie 1 vor dem Testen.

usr2564301
quelle
1

Ein Plädoyer für Lesbarkeit

Obwohl Sie bereits einige gute Antworten haben, möchte ich eine Programmiertechnik empfehlen, die Ihren Code für zukünftige Leser besser lesbar macht - das können Sie in sechs Monaten sein, ein Kollege, der gebeten wurde, eine Codeüberprüfung durchzuführen, Ihr Nachfolger, .. .

Dies dient dazu, alle "cleveren" Anweisungen in eine Funktion zu packen, die genau (mit ihrem Namen) zeigt, was sie tut. Während es einen winzigen Einfluss auf die Leistung gibt (von "Funktionsaufruf-Overhead"), ist dies in einer Spielsituation wie dieser wirklich vernachlässigbar.

Unterwegs können Sie Ihre Eingaben bereinigen - beispielsweise auf "illegale" Werte testen. So könnte es sein, dass Sie Code wie diesen erhalten - sehen Sie, wie viel besser lesbar er ist? Die "Hilfsfunktionen" können irgendwo versteckt sein (sie müssen nicht im Hauptmodul sein: aus ihrem Namen geht hervor, was sie tun):

#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
quelle
3
Versucht es nicht, es mit YESund zu weit zu schieben?NO ?
Rmobis
@Raphael_ - es könnte gut sein: Ich habe nur ein "zum Beispiel" gezeigt. Viele Leute benutzen offensichtlich wahr / falsch. Aber ich kann mich nie erinnern (weil verschiedene Sprachen unterschiedliche Konventionen verwenden): ist istTRUE , Trueoder true? Und welche Header-Dateien müsste ich, wenn überhaupt, in normales C aufnehmen? Also habe ich meine eigenen gerollt. Ich frage mich, ob das eine Ablehnung war ...
Floris
1

Für den ersten:

if (x % 10 == 0)

gilt für:

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

Für den zweiten:

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

wird beantragen für:

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

Wir tun x-1im Grunde zuerst, um zu bekommen:

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

Dann teilen wir sie durch 10, um zu erhalten:

1, 3, 5, ..

Wir prüfen also, ob dieses Ergebnis ungerade ist.

Khaled.K
quelle
1

Sie können Folgendes versuchen:

        // 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
quelle
In der OP-Frage bezieht sich die Prüfung für das Vielfache von 10 nicht auf die Bereichsprüfung, und in der Bereichsprüfung muss 20 mit Ihrem Code ((20/10)% 2) -> (im gleichen Bereich von 11 liegen). 2% 2) -> 0
Serpiton
0

Ich weiß, dass diese Frage so viele Antworten hat, aber ich werde meine trotzdem hierher werfen ...

Entnommen aus Steve McConnells Code Complete , 2. Auflage: "Stair-Step Access Tables:

Eine weitere Art des Tischzugangs ist die Treppenstufenmethode. Diese Zugriffsmethode ist nicht so direkt wie eine Indexstruktur, verschwendet jedoch nicht so viel Datenraum. Die in Abbildung 18-5 dargestellte allgemeine Idee von Treppenstufenstrukturen besteht darin, dass Einträge in einer Tabelle eher für Datenbereiche als für bestimmte Datenpunkte gültig sind.

Geben Sie hier die Bildbeschreibung ein

Abbildung 18-5 Der Treppenstufenansatz kategorisiert jeden Eintrag, indem er die Ebene bestimmt, auf der er auf eine „Treppe“ trifft. Der „Schritt“, den es trifft, bestimmt seine Kategorie.

Wenn Sie beispielsweise ein Benotungsprogramm schreiben, kann der Eingabebereich „B“ zwischen 75 und 90 Prozent liegen. Hier ist eine Reihe von Noten, die Sie möglicherweise eines Tages programmieren müssen:

Geben Sie hier die Bildbeschreibung ein

Um die Treppenstufenmethode zu verwenden, fügen Sie das obere Ende jedes Bereichs in eine Tabelle ein und schreiben dann eine Schleife, um eine Punktzahl gegen das obere Ende jedes Bereichs zu überprüfen. Wenn Sie den Punkt finden, an dem die Punktzahl zum ersten Mal die Spitze eines Bereichs überschreitet, wissen Sie, wie hoch die Note ist. Bei der Treppenstufentechnik müssen Sie darauf achten, die Endpunkte der Bereiche richtig zu handhaben. Hier ist der Code in Visual Basic, der einer Gruppe von Schülern anhand dieses Beispiels Noten zuweist:

Geben Sie hier die Bildbeschreibung ein

Obwohl dies ein einfaches Beispiel ist, können Sie es leicht verallgemeinern, um mehrere Schüler, mehrere Bewertungsschemata (z. B. unterschiedliche Noten für unterschiedliche Punktebenen bei unterschiedlichen Aufgaben) und Änderungen im Bewertungsschema zu behandeln. "

Code Complete , 2. Auflage, Seiten 426 - 428 (Kapitel 18).

lauCosma
quelle
Warum denkst du, dass dies die falsche Frage ist? Nur weil ich kein Beispiel für den OP-Fall gegeben habe, heißt das nicht, dass ich die falsche Frage beantwortet habe ...
lauCosma
0

Wie andere bereits betont haben, beschleunigt eine präzisere Gestaltung der Bedingungen weder die Kompilierung noch die Ausführung, und dies trägt auch nicht unbedingt zur Lesbarkeit bei.

Dies kann Ihnen dabei helfen, Ihr Programm flexibler zu gestalten, falls Sie später entscheiden, dass Sie eine Kleinkindversion des Spiels auf einem 6 x 6-Brett oder eine erweiterte Version (die Sie die ganze Nacht spielen können) auf einem 40 x 50-Brett wünschen .

Also würde ich es wie folgt codieren:

// 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
  }
}

Ja, es ist ausführlich, aber es macht deutlich, was genau auf dem Spielbrett passiert.

Wenn ich dieses Spiel für die Anzeige auf einem Telefon oder Tablet entwickeln würde, würde ich anstelle von Konstanten ROWS- und COLUMNS-Variablen erstellen, damit sie dynamisch (zu Beginn eines Spiels) an die Bildschirmgröße und -ausrichtung angepasst werden können.

Ich würde auch zulassen, dass die Bildschirmausrichtung jederzeit während des Spiels geändert wird - alles, was Sie tun müssen, ist, die Werte von ROWS und COLUMNS zu ändern, während Sie alles andere belassen (die aktuelle quadratische Zahl, auf der jeder Spieler ist, und die Start- / Endquadrate aller Schlangen und Leitern) unverändert. Dann müssen Sie "nur" die Tafel schön zeichnen und Code für Ihre Animationen schreiben (ich nehme an, das war der Zweck von Ihnenif Aussagen) ...

Laurence Renshaw
quelle
Ich habe auch Floris 'Antwort hochgestimmt - eine andere Art, ein ähnliches Ergebnis zu erzielen, die ich nicht gesehen habe, bevor ich meine Antwort geschrieben habe
Laurence Renshaw
2
Sie sollten Inline-Funktion anstelle von#define
Bryan Chen
Es ist empfehlenswert, wenn Sie funktionsähnliche #defineAnweisungen verwenden, um die Argumente in Klammern zu setzen, wo sie in der Erweiterung erscheinen. Also statt #define finished(num) (num == lastSquare)du solltest schreiben #define finished(num) ((num) == lastSquare). Der Grund dafür ist, dass Sie nicht die erwartete Antwort erhalten, wenn Sie eine solche Anweisung mit einem Ausdruck verwenden, der einen Operator mit einer ausreichend niedrigen Priorität enthält. In diesem Fall, wenn Sie nicht über die zusätzlichen Klammern verwenden, dann finished(a & b)dehnt sich in (a & b == lastSquare)die mit ziemlicher Sicherheit nicht das, was Sie wollen.
Dawood ibn Kareem