Home > database >  How can I have this grid wrap around on all sides?
How can I have this grid wrap around on all sides?

Time:01-29

I have an assignment where I need to let the user create the height and width of a grid. Then be able to place "ships" onto that grid at any x and y coordinates. Then you can place a storm on a certain section of that grid to then prompt and see if a storm is overlapped with a ship. I have done that successfully but I can't get the storm to wrap around the grid. For example if the grid is 20x20 and I put a storm that starts on the 20th spot over it should shift to the 1 position on the left side of the grid. Here is that code I have a diagram of what it looks like. (Note there is no physical grid getting printed to the terminal)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct ship {
    // Declaring variables
    char shipName[21]; 
    int x, y, w, h;  
    char direction;      
    struct ship *next;
} Ship;

//Declaring head of linked list
Ship *head = NULL;

void newShip(int x, int y, char direction, char *shipName) {
    //Adding ships to linked list
    Ship *new_ship = (Ship *)malloc(sizeof(Ship));
    strcpy(new_ship->shipName, shipName);
    new_ship->x = x;
    new_ship->y = y;
    new_ship->direction = direction;
    new_ship->next = head;
    head = new_ship;
}

void shipMovement(int time, int w, int h) {
    //Moving ships
    Ship *current = head;
    while (current != NULL) {
        if (current->direction = 'U') {
            current->y = (current->y   time) % h;
        }
        else if (current->direction = 'D') {
            current->y = (current->y - time   h) % h;
        }
        else if (current->direction = 'L') {
            current->x = (current->x - time   w) % w;
        }
        else if (current->direction = 'R') {
            current->x = (current->x   time) % w;
        }
        else {
            printf("Invalid Direction!\n");
        }
        current = current->next;
    }
}

void checkAffShips(int x, int y, int w, int h, char **affShips, int *numAffShips) {
    //Checking if ships are affected by storm
    Ship *current = head;
    while (current != NULL) {
        if (((current->x >= x) && (current->x < x   w)) && ((current->y >= y) && (current->y < y   h))) {
            // add shipName to list of affected ships
            affShips[*numAffShips] = (char *)malloc(sizeof(char) * 21);
            strcpy(affShips[(*numAffShips)  ], current->shipName);
        }
        current = current->next;
    }
}

//Main Function------------------------------------------------------------------
int main() {
    int w, h;
    int stormWidth, stormHeight;
    scanf("%d %d", &w, &h);
    char userInput;
    //Looping through commands
    while (scanf(" %c", &userInput) != 4) {
        if (userInput == '1') {
            int x, y;
            char direction[21], shipName[21];
            scanf("%d %d %s %s", &x, &y, direction, shipName);
            newShip(x, y, direction[0], shipName);
        } else if (userInput == '2') {
            int time;
            scanf("%d", &time);
            shipMovement(time, w, h);
        } else if (userInput == '3') {
            int x, y, w, h;
            scanf("%d %d %d %d", &x, &y, &w, &h);
            char *affShips[1000];
            int numAffShips = 0;
            checkAffShips(x, y, w, h, affShips, &numAffShips);
            printf("%d\n", numAffShips);
            for (int i = 0; i < numAffShips; i  ) {
                printf("%s\n", affShips[i]);
                free(affShips[i]);
            }
        } else if (userInput == '4') {
            break;
        }
        else {
            printf("Invalid Input!\n");
        }
    }
    return 0;
}

Here is the documentation: (https://i.stack.imgur.com/K28We.png)

I've tried a bunch of different stuff so maybe I just didn't do something right in my attempts but I'm sure someone can help figure this out.

CodePudding user response:

Your test for the affected ships is wrong. In your example, if you have a storm of size 2 in the bottom right corner (19, 19), a ship in the top left corner (0, 0) isn't caught, because 0 is not in the range [19, 21).

One way to fix this is to move the point you want to test into the "extended range" if it lies below the starting point of the storm. (The "extended range" is the range that does not wrap and so extends to two times the width of the board.)

So to test whether a one-dimensional point lies in a cyclic range, you could use a function like this:

int in_cyclic_range(int n, int lower, int width, int wrap)
{
    if (n < lower) n  = wrap;

    return (n < lower   width);
}

If you now test n in the whole range from [0, 20) on the storm at 19 of width 2, with in_cyclic_range(n, 19, 2, 20), you will get hits at locations 19 and 0.

CodePudding user response:

In order to test if a ship is affected by a storm, you need the storm position and dimensions, the ship position and the grid dimensions.

In your code, you use w, and h for the grid dimensions and a new set of variables w and h for the storm dimensions in the local scope of if (userInput == '3') { ... }. This is confusing and makes the grid dimensions inaccessible from this part of the code. You should use stormWidth and stormHeight for the storm dimensions and pass both these and the grid dimensions to the checkAffShips function.

The test in checkAffShips for the wrapped coordinate system is more complicated than what you wrote to take into account the wrapped parts of the storm area: as many as 3 extra rectangles.

Also note these issues:

  • using explicit names for the grid and storm dimensions would help avoid confusion.
  • the w and h fields in the Ship structure are unused.
  • reading strings with scanf should be protected by specifying the maximum number of characters to store into the destination array before the null terminator.
  • newShip should not use strcpy directly as the shipName argument could be longer than the destination array.
  • you should check the return value of scanf() to detect invalid or missing input.
  • if (current->direction = 'U') sets current->direction and evaluates to true. You should use == instead of =
  • the array of shipnames affShips should be allocated by checkAffShips.

Here is a modified version:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct ship {
    char shipName[21];
    int x, y;
    char direction;
    struct ship *next;
} Ship;

//Declaring head of linked list
Ship *head = NULL;

/* string copy function with size limitation */
void mystrcpy(char *dest, size_t size, const char *src) {
    while (size > 1) {
        *dest   = *src  ;
        size--;
    }
    if (size > 0)
        *dest = '\0';
}

void newShip(int x, int y, char direction, const char *shipName) {
    //Adding ship to linked list
    Ship *new_ship = (Ship *)malloc(sizeof(*new_ship));
    if (new_ship) {
        mystrcpy(new_ship->shipName, sizeof new_ship->shipName, shipName);
        new_ship->x = x;
        new_ship->y = y;
        new_ship->direction = direction;
        new_ship->next = head;
        head = new_ship;
    }
}

void shipMovement(int time, int w, int h) {
    //Moving ships
    for (Ship *current = head; current != NULL; current = current->next) {
        if (current->direction == 'U') {
            current->y = (current->y   time) % h;
        } else
        if (current->direction == 'D') {
            current->y = (current->y   h - time % h) % h;
        } else
        if (current->direction == 'L') {
            current->x = (current->x   w - time % w) % w;
        } else
        if (current->direction == 'R') {
            current->x = (current->x   time) % w;
        } else {
            printf("%s: invalid Direction '%c'!\n",
                   current->shipName, current->direction);
        }
    }
}

char **checkAffShips(int stormX, int stormY, int stormWidth, int stormHeight,
                     int gridWidth, int gridHeight,
                     int *pnumAffShips)
{
    char **affShips = NULL;
    int numAffShips = 0;
    for (Ship *current = head; current != NULL; current = current->next) {
        int x = current->x;
        int y = current->y;
        //Checking if ship is affected by storm
        if (((x >= stormX && x < stormX   stormWidth) ||
             (stormX   stormWidth > gridWidth && x < (stormX   stormWidth) % gridWidth)) &&
            ((y >= stormY && y < stormY   stormHeight) ||
             (stormY   stormHeight > gridHeight && y < (stormY   stormHeight) % gridHeight)))
        {
            // add shipName to list of affected ships
            affShips = realloc(affShips, sizeof(*affShips) * (numAffShips   1));
            affShips[numAffShips  ] = strdup(current->shipName);
        }
    }
    *pnumAffShips = numAffShips;
    return affShips;
}

//Main Function------------------------------------------------------------------
int main() {
    int gridWidth, gridHeight;
    if (scanf("%d %d", &gridWidth, &gridHeight) != 2)
        return 1;
    char userInput;
    //Looping through commands
    while (scanf(" %c", &userInput) == 1) {
        if (userInput == '1') {
            int x, y;
            char direction[21], shipName[21];
            if (scanf("%d %d  s  s", &x, &y, direction, shipName) != 4)
                return 1;
            newShip(x, y, direction[0], shipName);
        } else
        if (userInput == '2') {
            int time;
            if (scanf("%d", &time) != 1)
                return 1;
            shipMovement(time, gridWidth, gridHeight);
        } else
        if (userInput == '3') {
            int x, y, stormWidth, stormHeight;
            if (scanf("%d %d %d %d", &x, &y, &stormWidth, &stormHeight) != 4)
                return 1;
            char **affShips;
            int numAffShips = 0;
            affShips = checkAffShips(x, y, stormWidth, stormHeight,
                                     gridWidth, gridHeight, &numAffShips);
            printf("%d\n", numAffShips);
            for (int i = 0; i < numAffShips; i  ) {
                printf("%s\n", affShips[i]);
                free(affShips[i]);
            }
            free(affShips);
        } else
        if (userInput == '4') {
            break;
        } else {
            printf("Invalid Input!\n");
        }
    }
    return 0;
}
  • Related