Thread: How can I get the user to change the size of the board in Battleship?

  1. #1
    Registered User
    Join Date
    Mar 2012
    Posts
    33

    How can I get the user to change the size of the board in Battleship?

    I'm trying to do that as an extra credit option for my class. The teacher said to malloc the array, but I dont really understand how to do this. Any help is appreciated, thanks.

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    
    #define SIZE 10
    #define BAT_NO_SEL 0
    #define NOBAT_NO_SEL 1
    #define MISS 2
    #define HIT 3
    #define NUM_SHIPS 3
    
    void printHidden(int arr[SIZE][SIZE]);
    void printBoard(int arr[SIZE][SIZE]);
    void randomizeShips(int arr[SIZE][SIZE], int num_ships);
    int checkHit(int arr[SIZE][SIZE], int row, int col);
    int checkWin(int arr[SIZE][SIZE]);
    void initializeBoard(int arr[SIZE][SIZE]);
    
    int main (void) {
      
          
           int board[SIZE][SIZE];
           char letter;
           int number;
           int count = 0;
           srand((unsigned int) time (0));
    
           printf("Let's play Battleship!\n");
           printf("* is a hit\n");
           printf("o is a miss\n\n");
           initializeBoard(board);
           randomizeShips(board, NUM_SHIPS);
           printBoard(board);
           printHidden(board);
    
           while(checkWin(board) == 0 && count/2 <= 100) {
                fflush(stdin);
                printf("Enter letter coordinate:\n");
                scanf("%c", &letter);
                printf("Enter number coordinate:\n");
                scanf("%i", &number);
                if(checkHit(board, letter, number)== 4) {
                    count--;
                }
                printBoard(board);
                printHidden(board);   
                checkWin(board);
                count++;  
           }
        printf("Turns it took to win: %i\n", count);
    
    }
    //set all board values to no battleship/not selected
    void initializeBoard(int arr[SIZE][SIZE]) {
    
           int r, c;
           for(r = 0; r < SIZE; r++) {
                   for(c = 0; c < SIZE; c++) {
                           arr[r][c] = NOBAT_NO_SEL;
                   }
           }
    
    }
    //print out the board the user will see
    //print "~" if no battleship selected or battleship not selected
    //print "*" if hit
    //print "o" if miss
    void printBoard(int arr[SIZE][SIZE]) {
    
           int r,c,i;
    
           //Number coordinates
           printf("  ");
           for(i = 1; i < SIZE+1; i++) {
            if(i > 9) {
                printf("%i ", i);
            }
            else {
                printf("%i  ", i);
            }
           }
           printf("\n");
    
           for(r = 0; r < SIZE; r++) {
                   //Letter coordinates
                           printf("%c ", (char) (r+65));
    
                   for(c = 0; c < SIZE ; c++) {
    
                  
                           if(arr[r][c] == NOBAT_NO_SEL || arr[r][c] == BAT_NO_SEL) {
                                   printf("%s", "~  ");
                           }
                           else if(arr[r][c] == MISS ) {
                                   printf("%s", "o  ");
                           }
                           else if(arr[r][c] == HIT ) {
                                   printf("%s", "*  ");
                           }
                   }
                   printf("\n");
           }
     
    
    }
    void printHidden(int arr[SIZE][SIZE]) {
    
           int r, c;
           for(r = 0; r < SIZE; r++) {
                   for(c = 0; c < SIZE; c++) {
    
                           if(arr[r][c] == BAT_NO_SEL) {
                                   printf("%i", BAT_NO_SEL);
                           }
                           else if(arr[r][c] == NOBAT_NO_SEL) {
                                   printf("%i", NOBAT_NO_SEL);
                           }
                           else if(arr[r][c] == MISS ) {
                                   printf("%i", MISS);
                           }
                           else if(arr[r][c] == HIT ) {
                                   printf("%i", HIT );
                           }
                   }
                   printf("\n");
           }
    
      
    }
    void randomizeShips(int arr[SIZE][SIZE], int num_ships) {
    
        int horizontal, row, col, i, j;
     
        for(i = 0; i < num_ships; i++) {
            horizontal = i % 2;
                if(horizontal == 0) {
        
                    col = rand() % 8;
                    row = rand() % 10;    
            
     
                    if(arr[row][col] == BAT_NO_SEL) {
                            i--;
                            continue;
                    }
                    else if(arr[row][col+1] == BAT_NO_SEL) {
                            i--;
                            continue;
                    }
                    else if(arr[row][col+2] == BAT_NO_SEL) {
                            i--;
                            continue;
                    }
                    else {
                            for(j = 0; j < 3; j++) {
                                arr[row][col+j] = BAT_NO_SEL;        
                            }
                        }
        
                }
                else {
                        col = rand() % 8;
                        row = rand() % 10;
    
                    if(arr[col][row] == BAT_NO_SEL) {
                        i--;
                        continue;
                    }
                    else if(arr[col+1][row] == BAT_NO_SEL) {
                        i--;
                        continue;
                    }
                    else if(arr[col+2][row] == BAT_NO_SEL) {
                        i--;
                        continue;
                    }
                    else {
                        for(j = 0; j < 3; j++) {
                            arr[col+j][row] = BAT_NO_SEL;
                        }
                    }
                }
            }
    }
    int checkHit(int arr[SIZE][SIZE], int row, int col) {
    
        int i;
        col--;
    
        if(row >= 'A' && row <= 'J') {
            row = row -'A';
        }
        else if (row >= 'a' || row <= 'j') {
            row = row - 'a';
        }
        else {
            //Tell user if letter is out of bounds
            printf("**Letter and/or number out of bounds, try again!**\n");
            return 4;
        }
        //Tell user if number is out of bounds
        if(col > SIZE-1) {
            printf("**Letter and/or number out of bounds, try again!**\n");
            return 4;
        }
        //Tell user if position is already taken
        if(arr[row][col] == HIT || arr[row][col] == MISS) {
                printf("**Position already taken, try again!**\n");
                return 4;
            }
        //Tell user if they HIT or MISS
        for(i = 0; i < 100; i++) {
            if(arr[row][col] == BAT_NO_SEL) {
                arr[row][col] = HIT;
                printf("\n****HIT!****\n");
                return 3;
             }
             else if(arr[row][col] == NOBAT_NO_SEL) {
                    arr[row][col] = MISS;
                    printf("\n****MISS!****\n");
                    return 2;
             }
          
        }
    
    }
    int checkWin(int arr[SIZE][SIZE]) {
    
        int r, c, count = 0;
           for(r = 0; r < SIZE; r++) {
                   for(c = 0; c < SIZE; c++) {
                        if(arr[r][c] == HIT) {
                            count++;
                            if(count == 9)        
                            return 1;
                        }
                  }
           }
           return 0;
    }

  2. #2
    Programming Wraith GReaper's Avatar
    Join Date
    Apr 2009
    Location
    Greece
    Posts
    2,739
    You have two options. Either create an array of arrays of integers to address as arr[x][y] or create an array of integers and address it as array[x*SIZE + y]. The second option is easier on both allocating and freeing. Don't forget that SIZE will no longer be a macro, its value will vary.
    Devoted my life to programming...

  3. #3
    Novice
    Join Date
    Jul 2009
    Posts
    568
    Two things need to change: how the board is represented in your program and how you pass it around in your functions. Your board, instead of having a static size as defined by SIZE, will have to created at runtime, based on user input. Prototypes and headers for all functions will have to change to reflect the new way of how the board is defined.

    If you haven't covered structures, pointers and memory management already, you should read up on that. Otherwise any suggestions given here might be a little hard to follow. It's extra credit; which implies you have to do extra work. *shrug*

    To start you off, here's how I'd approach this. Feel free to ask questions about this *after* doing the required extra reading on topic mentioned previously.
    Code:
    /*
        Define a structure that'll represent your board. It contains a pointer 
        to the board itself, as well as its dimensions. It's much easier to 
        manage this as one unit, instead of separate bits of data.
    */
    struct board
    {
            int size_x;
            int size_y;
            
            int *board;
    };
    
    
    /*
        Set up a board, passing in its dimensions and getting back a pointer 
        to our previously defined `board` structure. It will be NULL if 
        something went wrong. You should test for that condition.
       
        It's good practice to delegate the setup of special structures to 
        separate functions. This eases their development, debugging and 
        inevitable modification.
    */
    struct board* get_board(int x, int y)
    {
            /*
                Allocate memory for structure itself.
            
                `malloc(sizeof(*x))` might seem a little weird if you're more 
                used to seeing something like `malloc(sizeof(type_name))`. Former 
                will always allocate correct amount of memory for type under-
                lying `x`, even if declaration of `x` changes.
            */
            struct board *b = malloc(sizeof(*b));
            if (b == NULL)
                    return b;
            
            /*
                Allocate memory for what will replace your array, and clean up 
                propery if something goes wrong.
            */
            b->board = malloc(sizeof(*(b->board)) * (x * y));
            if (b->board == NULL) {
                    free(b);
                    b = NULL;
                    return b;
            }
    
            /*
                Finish setup of board structure.
            */
            b->size_x = x;
            b->size_y = y;
            
            return b;
    }
    
    /*
        Release memory allocated for the board structure and the board itself.
        Again, it's good practice to delegate this to a separate function.
    */
    void release_board(struct board *b)
    {
            free(b->board);
            free(b);
    }
    Now all that remains is for you to figure out how to change your functions to work with the new representation. Hint: syntax for accessing elements of board "array" doesn't change; as array access decays into pointer + offset.
    Disclaimer: This post shows my ignorance at the time of its making. I claim ownership of but not responsibility for all errors in it. Reference at your own peril.

  4. #4
    Registered User claudiu's Avatar
    Join Date
    Feb 2010
    Location
    London, United Kingdom
    Posts
    2,094
    Also, do not fflush(stdin), that is undefined behaviour.
    1. Get rid of gets(). Never ever ever use it again. Replace it with fgets() and use that instead.
    2. Get rid of void main and replace it with int main(void) and return 0 at the end of the function.
    3. Get rid of conio.h and other antiquated DOS crap headers.
    4. Don't cast the return value of malloc, even if you always always always make sure that stdlib.h is included.

  5. #5
    Registered User
    Join Date
    May 2012
    Posts
    505
    There's a problem with C, which is that superfically 2d or 3d arrays look like basic syntax. Virtually every beginner's book includes an example in the first chapter. In fact they are quite advanced, and difficult to use.

    You will find that resizeable 2d arrays are almost unusable, because it just creates complexity after complexity once you ned to pass the arrays about from function to function. But a 2d array is just a 1d array in reality. You simply acces it like this

    Code:
    array[y*width+x] = 1; /* set element x,y to 1 */
    How does this tie up with malloc? Simply

    Code:
    board = malloc(width * height * sizeof(int));
    Now we've got a board of width * height integers. Now we can pass it about, simply passin the board pointer, the width, and the height to every function.

  6. #6
    Registered User hk_mp5kpdw's Avatar
    Join Date
    Jan 2002
    Location
    Northern Virginia/Washington DC Metropolitan Area
    Posts
    3,817
    Code:
    while(checkWin(board) == 0 && count/2 <= 100) {
        fflush(stdin);
        printf("Enter letter coordinate:\n");
        scanf("%c", &letter);
        printf("Enter number coordinate:\n");
        scanf("%i", &number);
        if(checkHit(board, letter, number)== 4) {
            count--;
        }
        printBoard(board);
        printHidden(board);  
        checkWin(board);
        count++; 
    }
    As mentioned by claudiu, you should drop the use of fflush(stdin). Your use of it currently is due to the last scanf call in the code posted above. This scanf call reads an integer but it has a side effect you are having to deal with... it leaves the newline character in the input buffer for the next execution of your while loop. This leftover newline character then causes problems when you get to the first scanf call which reads a character. A %c specifier in a scanf call reads a character... any character from the input buffer and this leftover newline character fits the bill as far as scanf is concerned. This side effect causes the scanf call to be skipped and unfortunately for newbs they don't know how to deal with this issue and feel as though they must resort to undefined behavior by calling fflush on an input stream to clear out the stray input. The fflush call does need to go but how do you then deal with the extra newline character. The answer is quite simple in that there is a trick to calling scanf using a %c format specifier that will clear out the stream of whitespace characters (spaces/tabs/newlines) before blocking(waiting) for user input. This is what that loop would look like minus the fflush call and with the scanf trick in place:
    Code:
    while(checkWin(board) == 0 && count/2 <= 100) {
        printf("Enter letter coordinate:\n");
        scanf(" %c", &letter);
        printf("Enter number coordinate:\n");
        scanf("%i", &number);
        if(checkHit(board, letter, number)== 4) {
            count--;
        }
        printBoard(board);
        printHidden(board);  
        checkWin(board);
        count++; 
    }
    The difference is subtle but important. The space prior to the %c format specifier instructs the scanf call to ignore any leading whitespace in the input stream. This will eliminate the stray newline character causing you problems and then wait for user input.
    "Owners of dogs will have noticed that, if you provide them with food and water and shelter and affection, they will think you are god. Whereas owners of cats are compelled to realize that, if you provide them with food and water and shelter and affection, they draw the conclusion that they are gods."
    -Christopher Hitchens

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Say what!?! Battleship board game to be made into a movie!?!?
    By hk_mp5kpdw in forum General Discussions
    Replies: 2
    Last Post: 07-28-2010, 08:44 AM
  2. Is it possible to change the board Theme?
    By RealTime in forum A Brief History of Cprogramming.com
    Replies: 19
    Last Post: 10-11-2008, 02:48 PM
  3. Linux board name change
    By frenchfry164 in forum A Brief History of Cprogramming.com
    Replies: 8
    Last Post: 09-09-2003, 10:09 PM