C++ Multiple Possible Value Control Flow - c++

Currently using C++20, GCC 11.1.0
I'm coding for simple movement in a game loop.
Following the abstract pseudocode below, how would I be able to translate this into code? I was thinking of using either goto to just skip right into the scope that uses the values, or std::optional to check whether the values exist or not.
The reason I'm trying to do this instead of just adding the bottom if statement into the A...D if statements is because the bottom if statement could become very large, and may add redundancy. Or should I just refactor the if statement into a separate function?
if (direction is left && ...)
{
int xVelocity {left_calculation...};
}
else if (direction is right && ...)
{
int xVelocity {right_calculation...};
}
else if (direction is up && ...)
{
int yVelocity {up_calculation...};
}
else if (direction is down && ...)
{
int yVelocity {down_calculation...};
}
if (x has a value or y has a value)
{
// Do something with those values...
}

You can represent optionality via std::option:
std::optional xVelocityOpt =
direction == left ? std::make_optional(left_calculation)
: direction == right ? std::make_optional(right_calculation)
: {};
std::optional yVelocityOpt =
direction == up ? std::make_optional(up_calculation)
: direction == down ? std::make_optional(down_calculation)
: {};
if (xVelocityOpt || yVelocityOpt)
{
// you can use the actual values as
// *xVelocityOpt and *yVelocityOpt
// ...
}
... but I'd also consider using simple int velocities and represent empty as 0 (if what you mean by the variables are delta v in physics).

If instead of x,y you use delta_x,delta_y for relative value change then your problem solves itself. Then your if is just:
int delta_x = 0;
int delta_y = 0;
...
if( delta_x | delta_y )
on_xy_changed(old_x + delta_x, old_y + delta_y);

Related

What's the fastest way to convert a 2D L-system to lines

I'm building a 3D graphics engine, and I want to draw 2D L-systems. But I noticed that this gets quite slow, once you increase the number of iterations. I'm searching a way to rapidly expand my L-system into a vector<Line>, with Line a class containing 2 points. this is my current code:
// LParser::LSystem2D contains the L-system (replacement rules, angle increase, etc..)
// the turtle is a class I use to track the current angle and position as I generate lines
// Lines2D is a std::list of Lines (with lines a class containing 2 points and a color)
void expand(char c, const LParser::LSystem2D &ls2D, Turtle &T, Lines2D &L2D, const Color &line_color, int max_depth,
int depth = 0) {
const std::string str = ls2D.get_replacement(c);
for (const auto &character: str) {
if (character == '+' || character == '-') {
T.angle += (-((character == '-') - 0.5) * 2) * ls2D.get_angle(); // adds or subtracts the angle
continue;
} else if (character == '(') {
T.return_pos.push({T.pos, T.angle}); // if a bracket is opened the current position and angle is stored
continue;
} else if (character == ')') {
T.pos = T.return_pos.top().first; // if a bracket is closed we return to the stored position and angle
T.angle = T.return_pos.top().second;
T.return_pos.pop();
continue;
} else if (max_depth > depth + 1) {
expand(character, ls2D, T, L2D, line_color, max_depth, depth + 1); // recursive call
} else {
// max depth is reached, we add the line to Lines2D
L2D.emplace_back(Line2D(
{T.pos, {T.pos.x + cos(toRadians(T.angle)), T.pos.y + sin(toRadians(T.angle))}, line_color}));
T.pos = {T.pos.x + cos(toRadians(T.angle)), T.pos.y + sin(toRadians(T.angle))};
};
}
}
Lines2D gen_lines(const LParser::LSystem2D &ls2D, const Color &line_color) {
std::string init = ls2D.get_initiator();
Lines2D L2D;
Turtle T;
T.angle = ls2D.get_starting_angle();
for (const auto &c:init) {
if (c == '+' || c == '-') {
T.angle += (-((c == '-') - 0.5) * 2) * ls2D.get_angle();
continue;
} else if (c == '(') {
T.return_pos.push({T.pos, T.angle});
continue;
} else if (c == ')') {
T.pos = T.return_pos.top().first;
T.angle = T.return_pos.top().second;
T.return_pos.pop();
continue;
}
expand(c, ls2D, T, L2D, line_color, ls2D.get_nr_iterations());
}
return L2D;
}
Alphabet = {L, R, F}
Draw = {
L -> 1,
R -> 1,
F -> 1
}
Rules = {
L -> "+RF-LFL-FR+",
R -> "-LF+RFR+FL-",
F -> "F"
}
Initiator = "L"
Angle = 90
StartingAngle = 0
Iterations = 4
L-system example
I couldn't think of any way to increase performance (significantly). I though about multihtreading but you would need to now your position at the beginning of every thread, but then you would need to expand al the previous character.
Is there a more efficient algorithm to do this task? Or a way to implement this so I could use multithreading?
EDIT: I've looked into the answers and this is what I came up with, this increased performance, but one drawback is that my program will use more ram(and I'm limited to 2GB, which is alot but still.) One solution is using a queue, but this decreases performance.
Lines2D LSystem2DParser::generateLines() {
Lines2D lines;
drawing = l_system2d.get_initiator();
Timer T;
expand();
T.endTimer("end of expand: ");
Timer T2;
lines = convert();
T2.endTimer("end of convert: ");
return lines;
}
void LSystem2DParser::expand() {
if (depth >= max_depth) {
return;
}
std::string expansion;
for (char c : drawing) {
switch (c) {
case '+':
case '-':
case '(':
case ')':
expansion += c;
break;
default:
expansion += replacement_rules[c];
break;
}
}
drawing = expansion;
depth++;
expand();
}
Lines2D LSystem2DParser::convert() {
Lines2D lines;
double current_angle = toRadians(l_system2d.get_starting_angle());
double x = 0, y = 0, xinc = 0, yinc = 0;
std::stack<std::array<double, 3>> last_pos;
for (char c: drawing){
switch (c) {
case('+'):
current_angle += angle;
xinc = cos(current_angle);
yinc = sin(current_angle);
break;
case ('-'):
xinc = cos(current_angle);
yinc = sin(current_angle);
break;
case ('('):
last_pos.push({x, y, current_angle});
break;
case (')'):
x = last_pos.top()[0];
y = last_pos.top()[1];
current_angle = last_pos.top()[2];
last_pos.pop();
break;
default:
lines.emplace_back(Line2D(Point2D(x,y), Point2D(x+xinc, y+yinc), line_color));
x += xinc;
y += yinc;
break;
}
}
return Lines2D();
}
EDIT 2:
It's still slow, in comparison to the code posted below
EDIT 3: https://github.com/Robin-Dillen/3DEngine all the code
EDIT 4: having a weird bug with a loop not ending
for (std::_List_const_iterator<Point2D> point = ps.begin(); point != ps.end(); point++) {
std::_List_const_iterator<Point2D> point2 = point++;
img.draw_line(roundToInt(point->x * d + dx), roundToInt(point->y * d + dy), roundToInt(point2->x * d + dx),
roundToInt(point2->y * d + dy), line_color.convert());
}
I have implemented a Lsystem to generate and draw a Sierpinski ( https://en.wikipedia.org/wiki/L-system#Example_5:_Sierpinski_triangle ) This is very similar to what your are doing. I have implemented in a straightforward way with no tricks. Here is the result of time profiling the code for an iteration depth of 11.
raven::set::cRunWatch code timing profile
Calls Mean (secs) Total Scope
1 0.249976 0.249976 draw
11 0.0220157 0.242172 grow
grow is the recursive function. It is called 11 times, with a mean execution time of 22 milliseconds.
draw is the function that takes the final string produced and draws it on the screen. This is called once and needs 250 msec.
The conclusion from this is that the recursive function does not require optimization, since 50% of the application time is used by the drawing.
In your question you do not provide time profiling data, nor even what you mean by "quite slow". I would say that if your code takes more than, say, 100 milliseconds to generate ( not draw ) the final string, then you have a problem which is being caused by a poor implementation of the standard algorithm. If, however, the slowness you complain of is dues to the drawing of the lines, then your problem is likely with a poor choice of graphics library - some graphics libraries do even simple things like drawing lines hundreds of time faster than others.
I invite you take a look at my code at
https://github.com/JamesBremner/Lindenmayer/blob/main/main.cpp
If you just want to parse the string and save the lines to a vector, then things go even faster since no graphics library is involved.
raven::set::cRunWatch code timing profile
Calls Mean (secs) Total Scope
11 0.00229241 0.0252165 grow
1 0.0066558 0.0066558 VectorLines
Here is the code
std::vector<std::pair<int,int> >
VectorLines( const std::string& plant )
{
raven::set::cRunWatch aWatcher("VectorLines");
std::vector<std::pair<int,int> > vL;
int x = 10;
int y = 10;
int xinc = 10;
int yinc = 0;
float angle = 0;
for( auto c : plant )
{
switch( c )
{
case 'A':
case 'B':
break;;
case '+':
angle += 1;
xinc = 5 * cos( angle );
yinc = 5 * sin( angle );
break;
case '-':
angle -= 1;
xinc = 5 * cos( angle );
yinc = 5 * sin( angle );
break;
}
x += xinc;
y += yinc;
vL.push_back( std::pair<int,int>( x, y ) );
}
return vL;
}
Generally speaking, the first step in optimizing the performance of an application is to profile the code to see where exactly the most time is being spent. Without this step, a lot of effort can be wasted optimizing code that actually has little impact on performance.
However, in this particular case, I would look to simplifying your code so it is easier to see what is going on and so make it easier to interpret the results of performance profiling.
Your recursive function expand could be streamlined by
Moving all those parameters out of the signature. There is no need to place so many copies on the same things on stack!
The first thing a recursive function should do is check if recursion is complete. In this case, check the depth.
The second thing, if further recursion is required, is perform the preparation of the next call. In this case, production of the next string from the current.
Finally, the recursive function can be called.
Below I will post code that implements Lindenmayer's original L-system for modelling the growth of algae. This is much simpler that what you are doing, but hopefully showsthe the method and benefit of re-organizing recursive code into the "standard" style of doing recursion.
Is there a more efficient algorithm to do this task?
I doubt it. I suspect that you could improve your implementation, but it is hard to know without profiling your code.
a way to implement this so I could use multithreading?
Recursive algorithms are not good candidates for multithreading.
Here is simple code implementing a similar recursive algorithm
#include <iostream>
#include <map>
using namespace std;
class cL
{
public:
cL()
: myAlphabet("AB")
{
}
void germinate(
std::map< char, std::string>& rules,
const std::string& axiom,
int generations )
{
myRules = rules;
myMaxDepth = generations;
myDepth = 0;
myPlant = axiom;
grow();
}
private:
std::string myAlphabet;
std::map< char, std::string> myRules;
int myDepth;
int myMaxDepth;
std::string myPlant;
std::string production( const char c )
{
if( (int)myAlphabet.find( c ) < 0 )
throw std::runtime_error(
"production character not in alphabet");
auto it = myRules.find( c );
if( it == myRules.end() )
throw std::runtime_error(
"production missing rule");
return it->second;
}
/// recursive growth
void grow()
{
// check for completion
if( myDepth == myMaxDepth )
{
std::cout << myPlant << "\n";
return;
}
// produce the next growth spurt
std::string next;
for( auto c : myPlant )
{
next += production( c );
}
myPlant = next;
// recurse
myDepth++;
grow();
}
};
int main()
{
cL L;
std::map< char, std::string> Rules;
Rules.insert(std::make_pair('A',std::string("AB")));
Rules.insert(std::make_pair('B',std::string("A")));
for( int d = 2; d < 10; d++ )
{
L.germinate( Rules, "A", d );
}
return 0;
}
L2D.emplace_back(Line2D(
{T.pos, {T.pos.x + cos(toRadians(T.angle)), T.pos.y + sin(toRadians(T.angle))}, line_color}));
T.pos = {T.pos.x + cos(toRadians(T.angle)), T.pos.y + sin(toRadians(T.angle))};
Without profiling it is hard to know how important this is. However:
Why not store the angle in radians, instead of converting it to radians over and over?
If using radians would be problematical somewhere else, at least do the conversion once and store in local
Would be a good idea to add a Line2D constructor that takes a Turtle reference as a parameter and does its own calculations.
Is the recalculation of T.pos needed? Isn't the recusion now complete?

Neat way how to cyclically iterate 4 enum class values in both directions in C++?

I have:
enum class orientation {
North,
East,
South,
West
};
I want to rotate its instance left (North => West) and right (West => North).
But I don't want to convert them to numbers, because it harms readability and intention and also jumping from the last number to first and back is strange.
I came up with lots of solutions, but all are kind of lame :(
Since they're in order:
constexpr auto rotate(orientation o, int n) -> orientation {
// convert to int
int dir = (int)o;
// rotate as an int
dir = (dir + n) % 4;
// account for negatives
if (dir < 0) {
dir += 4;
}
// and then convert back
return orientation{dir};
}
Which you can check:
static_assert(rotate(orientation::North, 1) == orientation::East);
static_assert(rotate(orientation::North, -1) == orientation::West);
I picked the integer to mean "number of 90 degree turns right" but you can adjust as suitable for your actual problem. Or add helper functions like:
constexpr auto rotate_left(orientation o) -> orientation {
return rotate(o, -1);
}
constexpr auto rotate_right(orientation o) -> orientation {
return rotate(o, 1);
}
Here's a solution that doesn't do any casting, and is quite easy to read:
constexpr auto rotate(orientation o, int n) -> orientation
{
n = (n + 4) % 4; // for negative rotations
if (!n)
return o;
switch (o)
{
case orientation::North : return rotate(orientation::East, n - 1);
case orientation::East : return rotate(orientation::South, n - 1);
case orientation::South : return rotate(orientation::West, n - 1);
case orientation::West : return rotate(orientation::North, n - 1);
}
}
The major advantage to this solution is that it's robust to someone coming along and changing the order of the members in the enum class.
Make a class called Orientation (say). Give it a member variable of the enumeration (orientation). Define a getter/setter, and increment and decrement operators (or rotate_left and rotate_right if you like those names better). Make this all implementation details inside the class.
You can use the logic that Barry suggested; but bury it in the class where no one has to deal with it.

Collision detection in voxel world

I am kinda stuck with my basic voxel physics right now. It's very, very choppy and I am pretty sure my maths is broken somewhere, but let's see what you have to say:
// SOMEWHERE AT CLASS LEVEL (so not being reinstantiated every frame, but persisted instead!)
glm::vec3 oldPos;
// ACTUAL IMPL
glm::vec3 distanceToGravityCenter =
this->entity->getPosition() -
((this->entity->getPosition() - gravityCenter) * 0.005d); // TODO multiply by time
if (!entity->grounded) {
glm::vec3 entityPosition = entity->getPosition();
if (getBlock(floorf(entityPosition.x), floorf(entityPosition.y), floorf(entityPosition.z))) {
glm::vec3 dir = entityPosition - oldPos; // Actually no need to normalize as we check for lesser, bigger or equal to 0
std::cout << "falling dir: " << glm::to_string(dir) << std::endl;
// Calculate offset (where to put after hit)
int x = dir.x;
int y = dir.y;
int z = dir.z;
if (dir.x >= 0) {
x = -1;
} else if (dir.x < 0) {
x = 1;
}
if (dir.y >= 0) {
y = -1;
} else if (dir.y < 0) {
y = 1;
}
if (dir.z >= 0) {
z = -1;
} else if (dir.z < 0) {
z = 1;
}
glm::vec3 newPos = oldPos + glm::vec3(x, y, z);
this->entity->setPosition(newPos);
entity->grounded = true; // If some update happens, grounded needs to be changed
} else {
oldPos = entity->getPosition();
this->entity->setPosition(distanceToGravityCenter);
}
}
Basic idea was to determine from which direction entityt would hit the surface and then just position it one "unit" back into that direction. But obviously I am doing something wrong as that will always move entity back to the point where it came from, effectively holding it at the spawn point.
Also this could probably be much easier and I am overthinking it.
As #CompuChip already pointed out, your ifs could be further simplified.
But what is more important is one logical issue that would explain the "choppiness" you describe (Sadly you did not provide any footage, so this is my best guess)
From the code you posted:
First you check if entity is grounded. If so you continue with checking if there is a collision and lastly, if there is not, you set the position.
You have to invert that a bit.
Save old position
Check if grounded
Set the position already to the new one!
Do collision detection
Reset to old position IF you registered a collision!
So basically:
glm::vec3 distanceToGravityCenter =
this->entity->getPosition() -
((this->entity->getPosition() - gravityCenter) * 0.005d); // TODO multiply by time
oldPos = entity->getPosition(); // 1.
if (!entity->grounded) { // 2.
this->fallingStar->setPosition(distanceToGravityPoint); // 3
glm::vec3 entityPosition = entity->getPosition();
if (getBlock(floorf(entityPosition.x), floorf(entityPosition.y), floorf(entityPosition.z))) { // 4, 5
this->entity->setPosition(oldPos);
entity->grounded = true; // If some update happens, grounded needs to be changed
}
}
This should get you started :)
I want to elaborate a bit more:
If you check for collision first and then set position you create an "infinite loop" upon first collision/hit as you collide, then if there is a collision (which there is) you set back to the old position. Basically just mathematic inaccuracy will make you move, as on every check you are set back to the old position.
Consider the if-statements for one of your coordinates:
if (dir.x >= 0) {
x = -1;
}
if (dir.x < 0) {
x = 1;
}
Suppose that dir.x < 0. Then you will skip the first if, enter the second, and x will be set to 1.
If dir.x >= 0, you will enter the first if and x will be set to -1. Now x < 0 is true, so you will enter the second if as well, and x gets set to 1 again.
Probably what you want is to either set x to 1 or to -1, depending on dir.x. You should only execute the second if when the first one was not entered, so you need an else if:
if (dir.x >= 0) {
x = -1;
} else if (dir.x < 0) {
x = 1;
}
which can be condensed, if you so please, into
x = (dir.x >= 0) ? -1 : 1;

Chess Validation Move input wanted

So, I have gotten quite far in my mission to finish a chess game in c++. However, I have hit a bit of a small issue I would like to get some input on, please.
SITUATION:
My PAWN, KING, KNIGHT move validations work perfect. But;
When moving a piece(such as a white ROOK) it follows most of the rules. For example, it will only move vertical or horizontal, it will not pass another white piece, it will not replace a white piece, and lastly it WILL replace a black (opposing) piece.
The problem is when moving it past a another black piece, it allows passing in order to replace a piece that's past it. So lets say we have a white piece at x=2,y=6 and black piece at x=2,y=4, and another black piece at x=2,y=3. The White piece will be allowed to move to move to x=2,y=3, which should not be allowed. Would love to get some input on how to fix this. Current code below.
bool Rook:: canMove(int startx, int starty, int endx, int endy)
{
int i;
if(board[endx][endy] !=NULL && board[endx][endy]->color==color)
return false;
if (startx == ends) //Collision Detection...
{
// Horizontal move
if (starty < endy)
{
// Move down
for (i = starty + 1; i <= endy; ++i)
if (board[startx][i] != NULL && board[startx][i]->color==color)
return false;
}
else
{
// Move up
for (i = starty - 1; i >= endy; --i)
if (board[startx][i] != NULL && board[startx][i]->color==color) //cant allow passing of non color piece
return false;
}
}
else if (starty == endy)
{
// Vertical move
if (startx < endx)
{
// Move right
for (i = startx + 1; i <= endx; ++i)
if (board[i][starty] != NULL && board[i][starty]->color==color)
return false;
}
else
{
// Move left
for (i = startx - 1; i >= endx; --i)
if (board[i][starty] != NULL && board[i][starty]->color==color)
return false;
}
}
else
{
// Not a valid rook move (neither horizontal nor vertical)
return false;
}
return true;
}
your function has refers to a lot of member variables in the class, e.g. ends, color, board, which isn't good, and makes the function hard to test at a unit level
can you test that function standalone? No you can't.
but it looks like your loops aren't breaking when they should (when they have found a valid move perhaps?)
if the function is allowing move to (2,3) as well as (2,4), then it is looping past (2,4) to (2,3)
also, just using an array and ints for indexing the board isn't very good.
i would have expected a higher-level board class and maybe a coordinate class so you can easily iterate and index the board.

Recursive Mine Explosion Function On a Board Game

I am trying to implement a board game on C++ and its some features are below:
I have 4 sources named as Mine (M), Water (W), Food (F) and Medical Supplies (S)
The Sources will be distributed to the board randomly (which I completed)
User will enter two coordinates and if there is mine on these coordinates they will just blow up and destroy the cells around them depending on their place. For example if the mine is on somewhere in the middle it will destroy the 8 cells around it and if there is another mine around the one which is exploded it will make the other one explode, too.
And there are some exceptions for example if the coordinate is on the corner it will just blow up 3 cell around it.
Let's come to the real problem. When I try to implement it I saw that it is tons of codes actually and I need to make it recursive to give the ability to blow up other cells so for every single possilibility I need to check if the blown cell is a mine or not. Is there an efficient way to implement this or do I need to just write the whole code?
void explode_mines(int x,int y) {
if (x == 0 && y == 0) {
grid[0][0] = 'X';
grid[0][1] = 'X';
if (grid[0][1] == 'X') explode_mines(0, 1);
grid[1][0] = 'X';
//...
grid[1][1] = 'X';
//...
}
//Is there any efficient way?
Pseudo code:
void ExploreCell(int x, int y)
{
if (x or y are out of bounds (less than zero/greater than max))
or (this cell is a mountain, because mountains don't explode))
return
else if this location is a mine
ExplodeMine(x, y) //This cell is a mine, so it blows up again
else
DestroyCell(x, y) //This cell is a valid, non-mine target
}
void ExplodeMine(int x, int y)
{
ExploreCell(x-1, y-1);
ExploreCell(x-1, y);
....
ExploreCell(x+1, y+1);
}
void DestroyCell(int x, int y)
{
//Take care of business
}
I think there's a typo in your code:
grid[0][1] = 'X';
if (grid[0][1] == 'X') explode_mines(0, 1);
How would location (0,1) not be 'X" at this point?
It doesn't have to be recursive, but information theory does say that you have to make 8 checks. You can make it more readable, however. For general purposes, I've found the basic perimeter check to be maintainable. Here, I'll let "O" be a crater and ""M" be a mine.
grid[x][y] = ' '
for (row = x-1; row <= x+1; row++) {
for (col = x-1; col <= x+1; col++) {
if grid[row][col] == "M"
explode_mines(row, col)
}
}
Now, if you have to worry about the time spent for a huge chain reaction, then you can alter your algorithm to keep two lists:
Squares that need checking
Squares with mines to blow up
In this case, explode_mines looks more like this:
Mark x,y as a dead square
Add adjacent squares to the checking list; do *not* add a duplicate
... and you get a new routine check_for_mine that looks like this:
while check list is not empty {
while mine list is not empty {
explode the top mine on the list
}
take the top square from the check list and check it
}
You can play with the nesting, depending on what chain-reaction order you'd like. For breadth-first explosions, you check all squares on the check list, then explode all the mines on the mine list; repeat that until both lists are empty. For depth-first, you can simplify the loops a little: explode every mine as soon as you find it, which means that you don't need a mine list at all.
Hoping this helps [caution: not tested] ('d' for "destroied", 'b' for "bomb")
void destroy (int x, int y)
{
char oldVal;
if ( (x >= 0) && (x < maxX) && (y >= 0) && (y < maxY)
&& ('d' != (oldVal = grid[x][y])) ) // 'd' for destroyed
{
grid[x][y] = 'd'; // set "destroyed"
if ( 'b' == oldVal ) // if it was a bomb, destroy surrounding
{
destroy(x-1, y-1);
destroy(x-1, y);
destroy(x-1, y+1);
destroy(x, y-1);
destroy(x, y+1);
destroy(x+1, y-1);
destroy(x+1, y);
destroy(x+1, y+1);
}
}
}