Skip to content
Snippets Groups Projects
Commit 9efc8f14 authored by William Fledelius's avatar William Fledelius
Browse files

Merge branch 'fix/allow-up-backwards-movement' into 'main'

Add rotation to movement model

See merge request !5
parents 6bfe5c4f 615078a9
No related branches found
No related tags found
1 merge request!5Add rotation to movement model
......@@ -11,10 +11,10 @@ Point::Point(const int x, const int y, const int z, const bool hydrophobic)
void Point::print() const {
std::cout << "Point (" << x << ", " << y << ", " << z << ")\n";
std::cout << (hydrophobic ? " - Hydrophobic (H)" : " - Polar (P)") << "\n";
//std::cout << (hydrophobic ? " - Hydrophobic (H)" : " - Polar (P)") << "\n";
}
bool Point::coords_match(Point other) {
bool Point::coords_match(Point other) const {
return other.x == x && other.y == y && other.z == z;
}
......@@ -23,6 +23,7 @@ bool Point::coords_match(Point other) {
HPModel::HPModel(const std::string &input_sequence) {
currentDirection = NORTH;
previousDirection = NORTH;
rotation = NO_ROTATION;
if (input_sequence.size() < 2) {
throw std::invalid_argument("Sequence length must be at least 2");
}
......@@ -68,6 +69,37 @@ bool HPModel::isInsertValid(Point toInsert) {
return true;
}
/**
* Updates the rotation in 90 degree steps
* POSITIVE = CLOCKWISE
* NEGATIVE = COUNTER CLOCKWISE
* @param amount amount of steps to rotate, for each step rotates 90 degrees, negative amount will rotate opposite direction
*/
void HPModel::rotate(int amount) {
// Since a rotation step is 90 degrees, every 4 rotations will be 360 degrees
int modAmount = amount % 4;
int rotationTimes = modAmount;
if (modAmount < 0) {
rotationTimes = 4 - (-modAmount);
}
for (int i = 0; i < rotationTimes; i++) {
switch (rotation) {
case NO_ROTATION:
rotation = ROTATE_90;
break;
case ROTATE_90:
rotation = ROTATE_180;
break;
case ROTATE_180:
rotation = ROTATE_270;
break;
case ROTATE_270:
rotation = NO_ROTATION;
break;
}
}
}
ABSOLUTE_DIRECTION
HPModel::getPlaneDirectionChange(ABSOLUTE_DIRECTION absolute_direction, DIRECTION relative_direction) {
if (absolute_direction == ABS_UP || absolute_direction == ABS_DOWN || relative_direction == UP ||
......@@ -108,16 +140,84 @@ HPModel::getPlaneDirectionChange(ABSOLUTE_DIRECTION absolute_direction, DIRECTIO
}
}
void HPModel::updateCurrentDirection(DIRECTION direction) {
/*
* Has the side effect of updating previousDirection, because this update is non-trivial
* Similarly updates the rotation, because up/down are the only folds that will do so
*/
ABSOLUTE_DIRECTION HPModel::getUpDownDirectionAndUpdatePrevious(DIRECTION relative_direction) {
if (relative_direction != UP && relative_direction != DOWN) {
throw std::invalid_argument("Invalid direction passed, must be either UP/DOWN\n");
}
ABSOLUTE_DIRECTION new_direction;
switch (direction) {
case FORWARD:
if (currentDirection != ABS_UP && currentDirection != ABS_DOWN) {
new_direction = currentDirection;
switch (currentDirection) {
case NORTH:
case EAST:
case SOUTH:
case WEST:
if (relative_direction == UP) {
// NORTH/EAST/SOUTH/WEST -> UP
new_direction = ABS_UP;
} else {
new_direction = previousDirection;
// NORTH/EAST/SOUTH/WEST -> DOWN
new_direction = ABS_DOWN;
}
// Note: in this case we DO NOT want to update the previous direction
break;
case ABS_UP:
switch (previousDirection) {
case NORTH:
case EAST:
case SOUTH:
case WEST:
if (relative_direction == UP) {
// UP -> UP
new_direction = invertDirection(previousDirection);
// After UP -> UP we will be "upside-down" and will need to rotate 180 degrees
rotate(2);
} else {
// UP -> DOWN
new_direction = previousDirection;
// No rotation required because this is equivalent to simply FORWARD
}
break;
case ABS_UP:
case ABS_DOWN:
throw std::invalid_argument("Both current and previous direction is UP/DOWN, illegal state\n");
}
break;
case ABS_DOWN:
switch (previousDirection) {
case NORTH:
case EAST:
case SOUTH:
case WEST:
if (relative_direction == UP) {
// DOWN -> UP
new_direction = previousDirection;
// No rotation required
} else {
// DOWN -> DOWN
new_direction = invertDirection(previousDirection);
// DOWN -> DOWN results in "upside-down" state just as UP -> UP does
rotate(-2);
}
break;
case ABS_UP:
case ABS_DOWN:
throw std::invalid_argument("Both current and previous direction is UP/DOWN, illegal state\n");
}
break;
}
return new_direction;
}
void HPModel::updateCurrentDirection(DIRECTION direction) {
ABSOLUTE_DIRECTION new_direction;
DIRECTION rotated_direction = getRotatedRelativeDirection(direction);
switch (rotated_direction) {
case FORWARD:
new_direction = currentDirection;
break;
case RIGHT:
case LEFT:
switch (currentDirection) {
......@@ -125,26 +225,58 @@ void HPModel::updateCurrentDirection(DIRECTION direction) {
case SOUTH:
case EAST:
case WEST:
new_direction = getPlaneDirectionChange(currentDirection, direction);
new_direction = getPlaneDirectionChange(currentDirection, rotated_direction);
break;
case ABS_UP:
case ABS_DOWN:
new_direction = getPlaneDirectionChange(previousDirection, direction);
// In this case we need to handle rotation changes, because moving "out" of an UP/DOWN forces a rotation
if (currentDirection == ABS_UP) {
// UP
if (rotated_direction == RIGHT) {
// UP -> RIGHT
rotate(1);
} else {
// UP -> LEFT
rotate(-1);
}
} else {
// DOWN
if (rotated_direction == RIGHT) {
// DOWN -> RIGHT
rotate(-1);
} else {
// DOWN -> LEFT
rotate(1);
}
}
new_direction = getPlaneDirectionChange(previousDirection, rotated_direction);
break;
default:
throw std::invalid_argument("Enum value: " + directionToString(direction) + " is invalid\n");
throw std::invalid_argument("Enum value: " + directionToString(rotated_direction) + " is invalid\n");
}
break;
case UP:
new_direction = ABS_UP;
//new_direction = ABS_UP;
new_direction = getUpDownDirectionAndUpdatePrevious(rotated_direction);
break;
case DOWN:
new_direction = ABS_DOWN;
//new_direction = ABS_DOWN;
new_direction = getUpDownDirectionAndUpdatePrevious(rotated_direction);
break;
default:
throw std::invalid_argument("Enum value: " + directionToString(direction) + " is invalid\n");
throw std::invalid_argument("Enum value: " + directionToString(rotated_direction) + " is invalid\n");
}
// ABS_UP/ABS_DOWN are handled directly in getUpDownDirectionAndUpdatePrevious
if ((new_direction == ABS_UP || new_direction == ABS_DOWN) && (currentDirection == ABS_UP || currentDirection == ABS_DOWN)) {
currentDirection = new_direction;
return;
}
if ((new_direction == ABS_UP || new_direction == ABS_DOWN) && (previousDirection == ABS_UP || previousDirection == ABS_DOWN)) {
previousDirection = currentDirection;
currentDirection = new_direction;
return;
}
if (new_direction != previousDirection) {
previousDirection = currentDirection;
}
......@@ -169,7 +301,8 @@ void HPModel::fold(DIRECTION direction) {
// At this point, there exists k folds, and no_of_points = k + 1 => k = no_of_points - 1
// We want k <= n - 1, thus we need, no_of_points - 1 < n - 1 => no_of_points < n
if (no_of_points >= sequence.size()) {
throw std::invalid_argument("Too many folds when trying to insert fold number " + std::to_string(no_of_points - 1));
throw std::invalid_argument(
"Too many folds when trying to insert fold number " + std::to_string(no_of_points - 1));
}
addPoint(direction, isCurrentAcidHydrophobic());
}
......@@ -222,3 +355,65 @@ void HPModel::print() const {
std::vector<Point> HPModel::getPoints() {
return acids;
}
ABSOLUTE_DIRECTION HPModel::invertDirection(ABSOLUTE_DIRECTION direction) {
switch (direction) {
case NORTH:
return SOUTH;
case EAST:
return WEST;
case SOUTH:
return NORTH;
case WEST:
return EAST;
case ABS_UP:
case ABS_DOWN:
throw std::invalid_argument("Unable to invert direction of ABS_UP/ABS_DOWN directions\n");
break;
}
}
DIRECTION rotateRelativeDirection90Degrees(DIRECTION relativeDirection) {
switch (relativeDirection) {
case FORWARD:
return FORWARD;
case UP:
return RIGHT;
case DOWN:
return LEFT;
case LEFT:
return UP;
case RIGHT:
return DOWN;
}
}
/**
* Rotates a relative direction based on the current rotation
*/
DIRECTION HPModel::getRotatedRelativeDirection(DIRECTION relative_direction) {
int rotateAmount;
switch (rotation) {
case NO_ROTATION:
rotateAmount = 0;
break;
case ROTATE_90:
rotateAmount = 1;
break;
case ROTATE_180:
rotateAmount = 2;
break;
case ROTATE_270:
rotateAmount = 3;
break;
}
DIRECTION new_relative_direction = relative_direction;
for (int i = 0; i < rotateAmount; i++) {
new_relative_direction = rotateRelativeDirection90Degrees(new_relative_direction);
}
return new_relative_direction;
}
\ No newline at end of file
......@@ -9,6 +9,7 @@
enum DIRECTION {FORWARD, UP, DOWN, LEFT, RIGHT};
enum ABSOLUTE_DIRECTION {NORTH, EAST, SOUTH, WEST, ABS_UP, ABS_DOWN};
enum ROTATION {NO_ROTATION, ROTATE_90, ROTATE_180, ROTATE_270};
class Point {
public:
......@@ -17,7 +18,7 @@ public:
Point(const int x, const int y, const int z, const bool hydrophobic);
void print() const;
bool coords_match(Point other);
bool coords_match(Point other) const;
};
class HPModel {
......@@ -26,13 +27,19 @@ private:
std::vector<Point> acids;
ABSOLUTE_DIRECTION currentDirection;
ABSOLUTE_DIRECTION previousDirection;
ROTATION rotation;
Point getHead();
static std::string directionToString(DIRECTION direction);
bool isInsertValid(Point toInsert);
bool isCurrentAcidHydrophobic();
ABSOLUTE_DIRECTION getPlaneDirectionChange(ABSOLUTE_DIRECTION absolute_direction, DIRECTION relative_direction);
static ABSOLUTE_DIRECTION getPlaneDirectionChange(ABSOLUTE_DIRECTION absolute_direction, DIRECTION relative_direction);
ABSOLUTE_DIRECTION getUpDownDirectionAndUpdatePrevious(DIRECTION relative_direction);
void updateCurrentDirection(DIRECTION direction);
void addPoint(DIRECTION direction, bool isHydrophobic);
void rotate(int amount);
DIRECTION getRotatedRelativeDirection(DIRECTION relative_direction);
static ABSOLUTE_DIRECTION invertDirection(ABSOLUTE_DIRECTION direction);
public:
std::vector<Point> getPoints();
void fold(DIRECTION direction);
......
......@@ -68,6 +68,86 @@ TEST(basic_check, test_planar) {
EXPECT_TRUE(p4.coords_match(p4Match));
}
TEST(basic_check, test_up_loop_valid) {
HPModel model = HPModel("pppp");
model.fold(UP);
model.fold(UP);
model.fold(UP);
auto points = model.getPoints();
// Initial Point
Point p0 = points[0];
Point p0Match = Point(0, 0, 0, false);
// Up
Point p1 = points[1];
Point p1Match = Point(0, 1, 0, false);
// Up
Point p2 = points[2];
Point p2Match = Point(0, 1, -1, false);
// Up
Point p3 = points[3];
Point p3Match = Point(0, 0, -1, false);
model.print();
EXPECT_TRUE(p0.coords_match(p0Match));
EXPECT_TRUE(p1.coords_match(p1Match));
EXPECT_TRUE(p2.coords_match(p2Match));
EXPECT_TRUE(p3.coords_match(p3Match));
}
TEST(basic_check, test_up_relative_valid) {
HPModel model = HPModel("ppppp");
model.fold(UP);
model.fold(RIGHT);
model.fold(LEFT);
model.fold(LEFT);
auto points = model.getPoints();
// Initial Point
Point p0 = points[0];
Point p0Match = Point(0, 0, 0, false);
// Up
Point p1 = points[1];
Point p1Match = Point(0, 1, 0, false);
// Right
Point p2 = points[2];
Point p2Match = Point(1, 1, 0, false);
// Left
Point p3 = points[3];
Point p3Match = Point(1, 2, 0, false);
// Left
Point p4 = points[4];
Point p4Match = Point(0, 2, 0, false);
model.print();
EXPECT_TRUE(p0.coords_match(p0Match));
EXPECT_TRUE(p1.coords_match(p1Match));
EXPECT_TRUE(p2.coords_match(p2Match));
EXPECT_TRUE(p3.coords_match(p3Match));
EXPECT_TRUE(p4.coords_match(p4Match));
}
TEST(basic_check, test_up_loop_invalid) {
HPModel model = HPModel("ppppp");
model.fold(UP);
model.fold(UP);
model.fold(UP);
EXPECT_THROW(model.fold(UP), std::invalid_argument);
model.print();
}
TEST(basic_check, test_up_down) {
HPModel model = HPModel("pppppp");
model.fold(RIGHT);
......@@ -91,15 +171,69 @@ TEST(basic_check, test_up_down) {
// Forward
Point p3 = points[3];
Point p3Match = Point(2, 1, 0, false);
Point p3Match = Point(1, 2, 0, false);
// Down
Point p4 = points[4];
Point p4Match = Point(2, 0, 0, false);
Point p4Match = Point(2, 2, 0, false);
// Left
Point p5 = points[5];
Point p5Match = Point(2, 0, 1, false);
Point p5Match = Point(2, 2, 1, false);
EXPECT_TRUE(p0.coords_match(p0Match));
EXPECT_TRUE(p1.coords_match(p1Match));
EXPECT_TRUE(p2.coords_match(p2Match));
EXPECT_TRUE(p3.coords_match(p3Match));
EXPECT_TRUE(p4.coords_match(p4Match));
EXPECT_TRUE(p5.coords_match(p5Match));
}
TEST(basic_check, big_test) {
HPModel model = HPModel("pppppppp");
model.fold(DOWN);
model.fold(DOWN);
model.fold(RIGHT);
model.fold(DOWN);
model.fold(RIGHT);
model.fold(UP);
model.fold(LEFT);
auto points = model.getPoints();
model.print();
// Initial point
Point p0 = points[0];
Point p0Match = Point(0, 0, 0, false);
// Down
Point p1 = points[1];
Point p1Match = Point(0, -1, 0, false);
// Down
Point p2 = points[2];
Point p2Match = Point(0, -1, -1, false);
// Right
Point p3 = points[3];
Point p3Match = Point(1, -1, -1, false);
// Down
Point p4 = points[4];
Point p4Match = Point(1, 0, -1, false);
// Right
Point p5 = points[5];
Point p5Match = Point(1, 0, 0, false);
// Up
Point p6 = points[6];
Point p6Match = Point(2, 0, 0, false);
// Left
Point p7 = points[7];
Point p7Match = Point(2, 1, 0, false);
EXPECT_TRUE(p0.coords_match(p0Match));
EXPECT_TRUE(p1.coords_match(p1Match));
......@@ -107,4 +241,45 @@ TEST(basic_check, test_up_down) {
EXPECT_TRUE(p3.coords_match(p3Match));
EXPECT_TRUE(p4.coords_match(p4Match));
EXPECT_TRUE(p5.coords_match(p5Match));
EXPECT_TRUE(p6.coords_match(p6Match));
EXPECT_TRUE(p7.coords_match(p7Match));
}
TEST(basic_check, right_up) {
HPModel model = HPModel("ppppp");
model.fold(RIGHT);
model.fold(UP);
model.fold(UP);
model.fold(RIGHT);
auto points = model.getPoints();
// Initial
Point p0 = points[0];
Point p0Match = Point(0, 0, 0, false);
// Right
Point p1 = points[1];
Point p1Match = Point(1, 0, 0, false);
// Up
Point p2 = points[2];
Point p2Match = Point(1, 1, 0, false);
// Up
Point p3 = points[3];
Point p3Match = Point(0, 1, 0, false);
// Right
Point p4 = points[4];
Point p4Match = Point(0, 1, -1, false);
model.print();
EXPECT_TRUE(p0.coords_match(p0Match));
EXPECT_TRUE(p1.coords_match(p1Match));
EXPECT_TRUE(p2.coords_match(p2Match));
EXPECT_TRUE(p3.coords_match(p3Match));
EXPECT_TRUE(p4.coords_match(p4Match));
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment