Okay, so I’m performing cellular updates on a grid representing an ocean. I have a method called update_grid() that needs to create a copy of the current Ocean object and use this.grid’s data and update temp_grid cells so that we avoid data dependency in different time steps:
/**
* @brief updates the grid
* @details First, generates a temporary copy of the current grid with all empty values
* Second, performs all updates on the copy grid while using data from old grid to avoid data dependency.
* Third, replaces the old grid with the new grid.
*/
void update_grid() {
// make a temporary Ocean object, a copied version of current object calling this function.
Ocean temp_ocean(*this);
// Iterate over this temp_ocean:
for (int i = 0; i < temp_ocean.num_rows; ++i) {
for (int j = 0; j < temp_ocean.num_cols; ++j) {
// skip empty cells
if (temp_ocean.get_cell(i, j) == Occupy::Empty) { continue; }
// retrieve cell state from current grid:
Occupy curr_obj = get_cell(i, j);
// perform movement based on the object type
switch(curr_obj) {
// when Turtle
case Occupy::Turtle:
temp_ocean.turtle_move(i, j);
break;
// when Trash
case Occupy::Trash:
temp_ocean.trash_move(i, j);
break;
// when Ship
case Occupy::Ship:
temp_ocean.ship_move(i, j);
break;
// default:
default: break;
}
}
}
// replace the original Ocean object with the temp one:
*this = temp_ocean;
};
This is my current draft. I’ll also paste other functions that are used.
/**
* @brief performs collision logic between two objects and updates the cell
* @param i row index of
* @param j column index
* @param direction the direction which the object will move to
* @param moving_obj the object that is trying to move to new (i, j) coordinate
*/
void collision(int i, int j, int new_i, int new_j, Occupy moving_obj) {
// get the object existing at the new location that will "crash" with moving_obj
Occupy existing_obj = get_cell(new_i, new_j);
CollisionResult collision_outcome = collision_logics[{moving_obj, existing_obj}];
// start performing collision logics:
switch (collision_outcome) {
// Case 1 - moving_obj can proceed to moving into new cell:
case CollisionResult::Move:
// current cell becomes empty:
set_cell(i, j, Occupy::Empty);
// new cell becomes the moving_obj:
set_cell(new_i, new_j, moving_obj);
break;
// Case 2 - moving_obj dies:
case CollisionResult::Die:
// current cell becomes empty:
set_cell(i, j, Occupy::Empty);
// new cell remains the same
break;
// Case 2 - moving_obj's move is Blocked:
// new_cell and old_cell all remain the same:
break;
default: break;
}
}
/**
* @brief updates coordinate to move the object to the desired position
* @param i row index
* @param j column index
* @param direction the direction which the object will move to
* @param object the Occupy obejct in the cell
*/
void move(int i, int j, Occupy object, Direction direction) {
// Exit early if the direction is Origin (no movement) or if object is empty
if (direction == Direction::Idle or object == Occupy::Empty) { return; }
// Initialize new x and y coordinates
int new_i = i, new_j = j;
// Determine the new coordinates based on the direction
// BE CAREFUL: the (i, j) coordinates are (-y, x) in our case, so be careful
// @details for example, "moving East" in our printed grid is increasing j index
switch (direction) {
case Direction::East: // East
new_j++;
break;
case Direction::North: // North
new_i--;
break;
case Direction::West: // West
new_j--;
break;
case Direction::South: // South
new_i++;
break;
case Direction::NorthEast: // North-East
new_i--;
new_j++;
break;
case Direction::NorthWest: // North-West
new_i--;
new_j--;
break;
case Direction::SouthEast: // South-East
new_i++;
new_j++;
break;
case Direction::SouthWest: // South-West
new_i++;
new_j--;
break;
default: break; // handles Origin case
}
// Check for out-of-bounds movement: if the new position is invalid, return early
if (new_i < 0 or new_i >= num_rows or new_j < 0 or new_j >= num_cols) {
return; // Object stays in the same position
}
// If within bounds, finish the moving logic with collision logic:
collision(i, j, new_i, new_j, object);
}
public:
//protected:
/**
* @brief generates random direction based on the user-defined probability for object staying idle
* @param idle_chance the probability that object stays idle
* @return random integer that corresponds to one of the moves in class Direction based on user-set parameters of probability
*/
Ocean::Direction random_direction(float idle_chance = (1. / 9.f)) {
// in total we have 9 moves total, with 1 move being idle and rest of 8 moves being active
// based on the given idle_chance, we return corresponding move with pre-defined probabilities
float random_num = random_float(0.0, 1.); // 100% percentage pdf
// simulate idle_chance probability:
if (random_num < idle_chance) {
// if probability satisfies, return Idle:
return Ocean::Direction::Idle;
} else {
// rest of the probability, return one of the active moves:
return static_cast<Ocean::Direction>(random_int(1, 8));
}
}
/**
* @brief generates movement for a turtle on the ocean grid
* @details all moves have equal chance of being executed from enum class Direction
* @param i row index
* @param j column index
*/
void turtle_move (int i, int j) {
// use random_direction () function with default prob of 1 / 9.
move(i, j, Occupy::Turtle, random_direction());
};
/**
* @brief generates movement for a trash on the ocean grid
* @details 50% chance of staying idle and 50% of moving in any random direction
* @param i row index
* @param j column index
*/
void trash_move (int i, int j) {
// use random_direction () function with idle prob of 50%.
move(i, j, Occupy::Trash, random_direction(0.5f));
};
/**
* @brief generates movement for a ship on the ocean grid
* @details 20% chance of staying idle and 80% of moving in any random direction
* @param i row index
* @param j column index
*/
void ship_move (int i, int j) {
// use random_direction () function with idle prob of 20%.
move(i, j, Occupy::Ship, random_direction(0.2f));
};
and finally getter and setter functions:
/**
* @brief setter for a specific cell
* @param i index of row, starting at 0
* @param j index of column, starting at 0
* @param value value to update that cell
*/
void set_cell(int i, int j, Ocean::Occupy value) {
grid.at(i * num_cols + j) = static_cast<int>(value);
}
/**
* @brief getter for a specific cell
* @param i index of row, starting at 0
* @param j index of column, starting at 0
* @return cell state in Occupy enum;
*/
Ocean::Occupy get_cell(int i, int j) {
return static_cast<Ocean::Occupy>(grid.at(i * num_cols + j));
}
My Question is within my update_grid(), i’m using this class methods directly at temp_ocean object and not “this.” Thus, will all the function invocation directly change the status of temp_ocean members or will they change the current object’s members? (this.grid for example)
By the way, all the methods are under class Ocean.
The main() would look something like this:
int main() {
Ocean ocean{// set up constructor};
ocean.update_grid();
return 0;
}