patterncppMinor
Simple console-based car game
Viewed 0 times
simplecargamebasedconsole
Problem
Below is my code for a simple console base car game. Is this a good approach or am I missing anything?
CarG.h
CarG.cpp
```
#include
#include
#include
#include
#include "CarG.h"
#define CC (x1==n && i==21) || (x1==n && i==22) || (x1==n && i==23) || (x1==n+1 && i==21) || (x1==n+1 && i==22) || (x1==n+1 && i==23) || (x1==n+2 && i==21) || (x1==n+2 && i==22) || (x1==n+2 && i==23) || (x1==n+3 && i==21) || (x1==n+3 && i==22) || (x1==n+3 && i==23) || (x1==n+4 && i==21) || (x1==n+4 && i==22) || (x1==n+4 && i==23)
using namespace std;
COORD coord={0,0};
void CarG::gotoxy(int x,int y)
{
coord.X=x;
coord.Y=y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),coord);
}
CarG::CarG()
{
srand(time(0));
cash = '$';
n=27;
points=0;
n1=0;
sleep=150;
}
void CarG::frame()
{
for(int j=1;j=42);
else
{ cls(); car(+2); }
}
char CarG::cfun()
{
for(;;)
{
int x1 = rand() % 30 + 17;
int x2 = rand() % 28 + 17;
if(x1==x2)
x1 = rand() % 31 + 17;
for(int i=0;i25)
sleep-=25;
else if(sleep>15 && sleep<25)
sleep-=3;
}
}
}
}
void CarG::displayscore()
{
gotoxy(50, 15);
cout << "points = "<< points;
}
char CarG::GameOver()
{
gotoxy(26,12);
cout << "GAME OVER";
gotoxy(23,13);
cout << "Your Score is : " << points;
gotoxy(19,15);
cout << "Want To Play
CarG.h
#ifndef CARG_H
#define CARG_H
class CarG
{
private:
int sleep;
int points;
int n, n1;
char cash, keyp;
public:
CarG();
void gotoxy(int x, int y);
void frame();
void car(int r);
void cls();
void movement(char keyp);
void lmove();
void rmove();
char cfun();
void displayscore();
void Ecar(int r, int x2);
void Ecls(int r, int x2);
char GameOver();
};
#endif // CARG_HCarG.cpp
```
#include
#include
#include
#include
#include "CarG.h"
#define CC (x1==n && i==21) || (x1==n && i==22) || (x1==n && i==23) || (x1==n+1 && i==21) || (x1==n+1 && i==22) || (x1==n+1 && i==23) || (x1==n+2 && i==21) || (x1==n+2 && i==22) || (x1==n+2 && i==23) || (x1==n+3 && i==21) || (x1==n+3 && i==22) || (x1==n+3 && i==23) || (x1==n+4 && i==21) || (x1==n+4 && i==22) || (x1==n+4 && i==23)
using namespace std;
COORD coord={0,0};
void CarG::gotoxy(int x,int y)
{
coord.X=x;
coord.Y=y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),coord);
}
CarG::CarG()
{
srand(time(0));
cash = '$';
n=27;
points=0;
n1=0;
sleep=150;
}
void CarG::frame()
{
for(int j=1;j=42);
else
{ cls(); car(+2); }
}
char CarG::cfun()
{
for(;;)
{
int x1 = rand() % 30 + 17;
int x2 = rand() % 28 + 17;
if(x1==x2)
x1 = rand() % 31 + 17;
for(int i=0;i25)
sleep-=25;
else if(sleep>15 && sleep<25)
sleep-=3;
}
}
}
}
void CarG::displayscore()
{
gotoxy(50, 15);
cout << "points = "<< points;
}
char CarG::GameOver()
{
gotoxy(26,12);
cout << "GAME OVER";
gotoxy(23,13);
cout << "Your Score is : " << points;
gotoxy(19,15);
cout << "Want To Play
Solution
A little disclaimer: I have not tested the code, so I'll assume it works as you intend and the game is playable.
A few insights on design and architecture
You say that this is a car game (a car-racing game I suppose), however, from looking at the code, there is nothing immediately obvious that corroborates that. You have only one class,
You should better separate concerns in your project. Have each class be a component of your game that does just one thing, but do this thing very well. Read about the Single Responsibility Principle.
Code review
Now focusing on the presented code:
-
Avoid abbreviated names that are unclear/hard to read.
Another point that fits in here is to use verbs or phrases that imply an action for function and method names. So
-
Still talking about naming, more care should should be taken when choosing your variables names. I know this is the hardest thing in programming, and that is specifically why we must strive for the best
Avoid single letter names, such as
For the record, there are a few cases where single letter variable names are fine. On such example are loop counters.
-
This is a personal preference, but I prefer to place the public section of a class first in the header file. My rationale is that people using and reading my code will care much more about the public methods of class, so that's what they want to see first when reading the header file. Private/protected sections of a class are of interest to the programmer maintaining the code, so it makes sense that they don't need that much visibility.
-
Avoid using macros. They can be quite problematic if misused. They can also be very easily misused. C++ offers a lot of other (better) options, like typed constants, enums and template functions. Talking specifics, your
-
There is a long discussion here about the issues of
-
-
Your code is filled with magic numbers. Try to move the ones that repeat themselves to named constants. The ones that are used in a single place, should at least be followed by a comment explaining why the given value was chosen.
-
Instead of using things like
-
You have used the old
for(int
A few insights on design and architecture
You say that this is a car game (a car-racing game I suppose), however, from looking at the code, there is nothing immediately obvious that corroborates that. You have only one class,
CarG that does everything. I would expect to see many other classes for specific things, like a Car class to handle car-related logic, a Console class responsible for drawing the track/cars and textual UI, a Game class to tie everything together and keep track of scores, save games, etc... Not limited to these examples.You should better separate concerns in your project. Have each class be a component of your game that does just one thing, but do this thing very well. Read about the Single Responsibility Principle.
Code review
Now focusing on the presented code:
-
Avoid abbreviated names that are unclear/hard to read.
CarG would be much better as CarGame. lmove should be moveLeft (I'm guessing), same for rmove => moveRight. Some methods of your class are just impossible to guess. For instance cfun. That is a very unclear name. Ecar and Ecls are not much better... The point here is to give descriptive names. Don't worry about the name length, that's not a problem nowadays, we have the technology of auto-complete. Also, make sure to write them in a way that facilitates reading, meaning to use some notation to separate words in a name, like snake_case or camelCase. So don't write names like displayscore, use display_score or displayScore instead.Another point that fits in here is to use verbs or phrases that imply an action for function and method names. So
displayScore() is good, but movement() not so much. Something like moveCars() would be better.-
Still talking about naming, more care should should be taken when choosing your variables names. I know this is the hardest thing in programming, and that is specifically why we must strive for the best
;)Avoid single letter names, such as
n and n1. sleep is also pretty vague. You have a lot of xs numbered as x1, x2, ... This can be very confusing. Take a step back and try to think of more descriptive names. For the record, there are a few cases where single letter variable names are fine. On such example are loop counters.
i, j and k, for instance, are of a historical relevance, so they are basically standard, you don't have to worry about that.-
This is a personal preference, but I prefer to place the public section of a class first in the header file. My rationale is that people using and reading my code will care much more about the public methods of class, so that's what they want to see first when reading the header file. Private/protected sections of a class are of interest to the programmer maintaining the code, so it makes sense that they don't need that much visibility.
-
Avoid using macros. They can be quite problematic if misused. They can also be very easily misused. C++ offers a lot of other (better) options, like typed constants, enums and template functions. Talking specifics, your
CC macro looks quite useless. The code that is wraps is only used in one place, so it is not avoiding code duplication, but only obfuscating it.-
There is a long discussion here about the issues of
using namespace std;. In short, it is not well seen because it defeats the purpose of a namespace, which is allowing duplicate names to coexist without clashes.-
COORD coord={0,0}; is a global variable for no good reason. It should be a local inside gotoxy().-
Your code is filled with magic numbers. Try to move the ones that repeat themselves to named constants. The ones that are used in a single place, should at least be followed by a comment explaining why the given value was chosen.
-
Instead of using things like
char(219), it would be better to type the character literal. If your code editor supports Unicode text, you should be able to. Otherwise, turn that into a named constant.-
You have used the old
rand() function and friends. You should also take a look at the new ` library introduced by C++11.
-
Properly indent code (specially loops) to convey nesting. This:
for(int k=-3;k<=4;k++)
for(int j=0;j<=3;j++)
Is a nice warm place for bugs to grow, forgive the pun. Nest the second loop under the first one. I also recommend always adding a pair of { } even for the single line statements, to facilitate future maintenance and expansion.
-
I would try to keep a more consistent code layout. Things like this:
else if(keyp=='n' || keyp=='N')
{ return 'q'; }
else
{ GameOver(); }
Break the pattern and make the flow of logic harder to follow.
-
Use consistent spacing between arithmetical operators. Putting a space between operator and operand(s) greatly facilitates reading. Take this for instance:
``for(int
Code Snippets
for(int k=-3;k<=4;k++)
for(int j=0;j<=3;j++)else if(keyp=='n' || keyp=='N')
{ return 'q'; }
else
{ GameOver(); }for(int j=1;j<=3;j+=2)for (int j = 1; j <= 3; j += 2)Context
StackExchange Code Review Q#77508, answer score: 6
Revisions (0)
No revisions yet.