Thread: C 2d Array, game board.

  1. #1
    Registered User
    Join Date
    Mar 2017
    Posts
    26

    C 2d Array, game board.

    I have this small program, i am trying to replicate naughts and crosses, AKA 3 in a row. I tried to write a function that checks all directions on the grid, but for some reason right now the function returns 1 when it shouldn't.How should i be going about this?

    Here's the sample I'm working with to test.

    Code:
    #include <stdio.h>
    #define GRID_SIZE 3
    
    int checkMove(char *, int, int);
    char *arr[GRID_SIZE][GRID_SIZE];
    
    int main() {
        for (int a = 0; a < GRID_SIZE; a++) {
            for (int b = 0; b < GRID_SIZE; b++) {
                arr[a][b] = "[*]";
            }
        }
    
        printf("\n");
    
        char *p = "[X]";
        arr[2][2]=p;
        arr[0][2]=p;
        int y=1, x=0;
        arr[y][x] = p;
    
        for (int a = 0; a < GRID_SIZE; a++) {
            for (int b = 0; b < GRID_SIZE; b++) {
                printf("%s", arr[a][b]);
            }
            printf("\n");
        }
    
        printf("%d\n", checkMove(p, y, x));
        return 0;
    }
    
    int checkMove(char *p, int y, int x) {
        if (arr[(y+1)][(x+1)]&&arr[(y-1)][(x-1)]||
            arr[(y+1)][(x-1)]&&arr[(y-1)][(x+1)]||
            arr[(y+1)][(x+1)]&&arr[(y+2)][(x+2)]||
            arr[(y-1)][(x-1)]&&arr[(y-2)][(x-2)]||
            arr[(y-1)][(x+1)]&&arr[(y-2)][(x+2)]||
            arr[(y+1)][(x-1)]&&arr[(y+2)][(x-2)]||
            arr[(y+1)][x]&&arr[(y-1)][x]||
            arr[(y+1)][x]&&arr[(y+2)][x]||
            arr[(y-1)][x]&&arr[(y-2)][x]||
            arr[y][(x+1)]&&arr[y][((x-1))]||
            arr[y][(x+1)]&&arr[y][(x+2)]||
            arr[y][(x-1)]&&arr[y][(x-2)]==p) {
            return 1;
        } else {
            return 0;
        }
    }
    Last edited by Strobez; 04-26-2017 at 12:44 AM.

  2. #2
    Registered User
    Join Date
    Mar 2017
    Posts
    26
    I managed to solve this, by adding bounds checking in another helper function. If anyone can suggest a better way please inform me.

    Code:
    int checkMove(char *p, int y, int x) {
        if (arr[(y+1)][(x+1)]&&arr[(y-1)][(x-1)]==p&&inbounds(y+1, x+1)&&inbounds(y-1, x-1)||
            arr[(y+1)][(x-1)]&&arr[(y-1)][(x+1)]==p&&inbounds(y+1, x-1)&&inbounds(y-1, x+1)||
            arr[(y+1)][(x+1)]&&arr[(y+2)][(x+2)]==p&&inbounds(y+1, x+1)&&inbounds(y+2, x+2)||
            arr[(y-1)][(x-1)]&&arr[(y-2)][(x-2)]==p&&inbounds(y-1, x-1)&&inbounds(y-2, x-2)||
            arr[(y-1)][(x+1)]&&arr[(y-2)][(x+2)]==p&&inbounds(y-1, x+1)&&inbounds(y-2, x+2)||
            arr[(y+1)][(x-1)]&&arr[(y+2)][(x-2)]==p&&inbounds(y+1, x-1)&&inbounds(y+2, x-2)||
            arr[(y+1)][x]&&arr[(y-1)][x]==p&&inbounds(y+1, x)&&inbounds(y-1, x)||
            arr[(y+1)][x]&&arr[(y+2)][x]==p&&inbounds(y+1, x)&&inbounds(y+2, x)||
            arr[(y-1)][x]&&arr[(y-2)][x]==p&&inbounds(y-1, x)&&inbounds(y-2, x)||
            arr[y][(x+1)]&&arr[y][((x-1))]==p&&inbounds(y, x+1)&&inbounds(y, x-1)||
            arr[y][(x+1)]&&arr[y][(x+2)]==p&&inbounds(y, x+1)&&inbounds(y, x+2)||
            arr[y][(x-1)]&&arr[y][(x-2)]==p&&inbounds(y, x-1)&&inbounds(y, x-2)) {
            return 1;
        } else {
            return 0;
        }
    }
    
    int inbounds (int y, int x) {
        return (y >= 0 && y < GRID_SIZE && x >= 0 && x < GRID_SIZE);
    }

  3. #3
    Registered User
    Join Date
    Mar 2017
    Posts
    26
    Maybe not, still doesn't work >.<

  4. #4
    Registered User
    Join Date
    Mar 2017
    Posts
    26
    I was missing the ==p on the first comparison.. Stupid mistake.
    Here's where i am at, working as expected so far.

    Code:
    int checkMove(char *p, int y, int x) {
        if (arr[(y+1)][(x+1)]==p&&arr[(y-1)][(x-1)]==p||
            arr[(y+1)][(x-1)]==p&&arr[(y-1)][(x+1)]==p||
            arr[(y+1)][(x+1)]==p&&arr[(y+2)][(x+2)]==p||
            arr[(y-1)][(x-1)]==p&&arr[(y-2)][(x-2)]==p||
            arr[(y-1)][(x+1)]==p&&arr[(y-2)][(x+2)]==p||
            arr[(y+1)][(x-1)]==p&&arr[(y+2)][(x-2)]==p||
            arr[(y+1)][(x)]==p&&arr[(y-1)][(x)]==p||
            arr[(y+1)][(x)]==p&&arr[(y+2)][(x)]==p||
            arr[(y-1)][(x)]==p&&arr[(y-2)][(x)]==p||
            arr[(y)][(x+1)]==p&&arr[(y)][(x-1)]==p||
            arr[(y)][(x+1)]==p&&arr[(y)][(x+2)]==p||
            arr[(y)][(x-1)]==p&&arr[(y)][(x-2)]==p) {
            return 1;
        } else {
            return 0;
        }
    }
    Last edited by Strobez; 04-26-2017 at 02:06 AM.

  5. #5
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    > arr[(y+1)][(x+1)]==p&&arr[(y-1)][(x-1)]==p&&inbounds(y+1, x+1)&&inbounds(y-1, x-1)
    You should do the inbound checks before trying to access out of bounds.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  6. #6
    Registered User
    Join Date
    Mar 2017
    Posts
    26
    Okay, thanks. I was doing bounds checking on one attempt but then it worked without once i fixed the obvious mistake, so i wanted to keep it short. But if it's the better way then I'll revert back to doing that.

  7. #7
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    You've implemented the board in a weird way. Normally it would be a board of characters, not pointers to strings that only differ in one character. And of course the board shouldn't be global (and arr is a name that only a pirate would love).

    Also, you can simplify checkMove and avoid out-of-bounds checks.
    Code:
    #include <stdio.h>
    
    #define GRID_SIZE 3
     
    void initGrid(char grid[][GRID_SIZE]);
    void printGrid(char grid[][GRID_SIZE]);
    int checkMove(char grid[][GRID_SIZE], int y, int x);
    
    int main(void) {
        char grid[GRID_SIZE][GRID_SIZE];
        char p = 'X';
        int x, y;
    
        initGrid(grid);
        grid[2][2] = p;
        grid[0][2] = p;
        y = 1, x = 0;
        grid[y][x] = p;
        printGrid(grid);
        printf("%d\n", checkMove(grid, y, x));
    
        initGrid(grid);
        grid[0][2] = p;
        grid[2][0] = p;
        y = 1, x = 1;
        grid[y][x] = p;
        printGrid(grid);
        printf("%d\n", checkMove(grid, y, x));
    
        initGrid(grid);
        grid[2][2] = p;
        grid[2][0] = p;
        y = 2, x = 1;
        grid[y][x] = p;
        printGrid(grid);
        printf("%d\n", checkMove(grid, y, x));
    
        return 0;
    }
    
    void initGrid(char grid[][GRID_SIZE]) {
        for (int a = 0; a < GRID_SIZE; a++)
            for (int b = 0; b < GRID_SIZE; b++)
                grid[a][b] = '*';
        // or memset(grid, '*', GRID_SIZE * GRID_SIZE);
    }
    
    void printGrid(char grid[][GRID_SIZE]) {
        for (int a = 0; a < GRID_SIZE; a++) {
            for (int b = 0; b < GRID_SIZE; b++)
                printf("[%c]", grid[a][b]);
            printf("\n");
        }
    }
     
    int checkMove(char grid[][GRID_SIZE], int y, int x) {
        char p = grid[y][x];
        return ((grid[y][0]==p && grid[y][1]==p && grid[y][2]==p) ||
                (grid[0][x]==p && grid[1][x]==p && grid[2][x]==p) ||
                (grid[1][1]==p &&
                    ((grid[0][0]==p && grid[2][2]==p) ||
                     (grid[0][2]==p && grid[2][0]==p))));
    }

  8. #8
    Registered User
    Join Date
    Mar 2017
    Posts
    26
    Thanks, i simply chose arr for the board out of convenience, it was just intended to be a proof of concept in a way as I've not done much in the way of game programming and such.
    I am bounds checking each move before checkMove is actually called.
    And that is a good method to use in checkMove, I wish i had of thought of it in that way, only that would fail if for some reason one chose to make the grid larger than 3x3, that's probably never going to be the case though so i appreciate it as i will probably use that.

  9. #9
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    Your method would also "fail" if the board wasn't 3x3. To make the check work for any GRID_SIZE, you'd have to use loops:
    Code:
    int checkMove(char grid[][GRID_SIZE], int y, int x) {
        char p = grid[y][x];
        int i;
    
        // Check vertical.
        for (i = 0; i < GRID_SIZE; i++)
            if (grid[i][x] != p)
                break;
        if (i == GRID_SIZE)
            return 1;
    
        // Check horizontal.
        for (i = 0; i < GRID_SIZE; i++)
            if (grid[y][i] != p)
                break;
        if (i == GRID_SIZE)
            return 1;
    
        // Check diagonal upper-left to lower-right.
        for (i = 0; i < GRID_SIZE; i++)
            if (grid[i][i] != p)
                break;
        if (i == GRID_SIZE)
            return 1;
    
        // Check diagonal upper-right to lower-left.
        for (i = 0; i < GRID_SIZE; i++)
            if (grid[i][GRID_SIZE - i - 1] != p)
                break;
        if (i == GRID_SIZE)
            return 1;
    
        return 0;
    }

  10. #10
    Registered User
    Join Date
    Mar 2017
    Posts
    26
    if only checking for 3 pieces on n*n it seems to work on any size, i tried just now with GRID_SIZE 5 and check for 3, it still ends the game.
    Could you explain at what point you would expect it to fail?
    Disregard the redundant parenthesis, i changed it so it no longer uses strings, added brackets in the print statement instead, (duh).. Here's what i have so far:
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    
    #define GRID_SIZE 5
    #define CROSS -1
    #define NAUGHT 1
    
    typedef struct {
        int y, x, last_player, players[2];
        char arr[GRID_SIZE][GRID_SIZE], p[2];
    } mStruct;
    
    int random_player();
    int inbounds (int, int);
    int checkMove(mStruct *m);
    void init_grid(mStruct *m);
    void print_board(mStruct *m);
    
    int main() {
        mStruct m;
        m.p[0] = 'X';
        m.p[1] = 'O';
        m.players[0] = CROSS;
        m.players[1] = NAUGHT;
        m.last_player = m.players[random_player()];
        init_grid(&m);
        do {
            printf("[%c], your turn: ", m.last_player==m.players[0] ? m.p[0]:m.p[1]);
            scanf("%d %d", &m.y, &m.x);
            if (inbounds(m.y, m.x)&&m.arr[m.y][m.x]=='*') {
                m.arr[m.y][m.x] = m.last_player == m.players[0] ? m.p[0] : m.p[1];
                print_board(&m);
                m.last_player *= -1;
            } else {
                printf("Invalid move!\n\n");
            }
        } while (!checkMove(&m));
        printf("\n[%c] wins!\n", -m.last_player==m.players[0] ? m.p[0]:m.p[1]);
        return 0;
    }
    
    int checkMove(mStruct *m) {
        int y=m->y, x=m->x;
        char p = -m->last_player==m->players[0] ? m->p[0]:m->p[1];
        return (m->arr[(y+1)][(x+1)]==p&&m->arr[(y-1)][(x-1)]==p||
                m->arr[(y+1)][(x-1)]==p&&m->arr[(y-1)][(x+1)]==p||
                m->arr[(y+1)][(x+1)]==p&&m->arr[(y+2)][(x+2)]==p||
                m->arr[(y-1)][(x-1)]==p&&m->arr[(y-2)][(x-2)]==p||
                m->arr[(y-1)][(x+1)]==p&&m->arr[(y-2)][(x+2)]==p||
                m->arr[(y+1)][(x-1)]==p&&m->arr[(y+2)][(x-2)]==p||
                m->arr[(y+1)][(x)]==p&&m->arr[(y-1)][(x)]==p||
                m->arr[(y+1)][(x)]==p&&m->arr[(y+2)][(x)]==p||
                m->arr[(y-1)][(x)]==p&&m->arr[(y-2)][(x)]==p||
                m->arr[(y)][(x+1)]==p&&m->arr[(y)][(x-1)]==p||
                m->arr[(y)][(x+1)]==p&&m->arr[(y)][(x+2)]==p||
                m->arr[(y)][(x-1)]==p&&m->arr[(y)][(x-2)]==p);
    }
    
    int inbounds (int y, int x) {
        return y >= 0 && y < GRID_SIZE && x >= 0 && x < GRID_SIZE;
    }
    
    int random_player() {
        srand((unsigned int) time(NULL));
        return rand()%2;
    }
    
    void print_board(mStruct *m) {
        printf("\n");
        for (int a = 0; a < GRID_SIZE; a++) {
            for (int b = 0; b < GRID_SIZE; b++) {
                printf("[%c]", m->arr[a][b]);
            }
            printf("\n");
        }
        printf("\n");
    }
    
    void init_grid(mStruct *m) {
        for (int a = 0; a < GRID_SIZE; a++) {
            for (int b = 0; b < GRID_SIZE; b++) {
                m->arr[a][b] = '*';
                printf("[%c]", m->arr[a][b]);
            }
            printf("\n");
        }
    
        printf("\n");
    }
    Last edited by Strobez; 04-26-2017 at 09:55 PM. Reason: &&m.arr[m.y][m.x]=='*'

  11. #11
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    Whatever. You are, of course, allowed to invent any arbitrary rules you wish.

    There are quite a few problems with your code, but since you've ignored my previous advice it's clearly not worth my time to point them out.

  12. #12
    Registered User
    Join Date
    Mar 2017
    Posts
    26
    I have not ignored it at all, it just got me wondering why, this code is in no way for production use i am just trying to get a good idea what the problem is, you suggested change from using strings, i did that, however you simply gave me another way and did not explain what the flaws are with the other ways I've tried?

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Board Game
    By SenoritaJ in forum C++ Programming
    Replies: 3
    Last Post: 07-30-2015, 08:01 AM
  2. Replies: 11
    Last Post: 11-17-2013, 02:57 AM
  3. Board game in C
    By sssk9797 in forum C Programming
    Replies: 85
    Last Post: 04-04-2012, 07:10 AM
  4. Board Game
    By Tiago in forum C Programming
    Replies: 4
    Last Post: 04-10-2010, 09:33 AM
  5. Board Game
    By Govtcheez in forum A Brief History of Cprogramming.com
    Replies: 26
    Last Post: 08-17-2001, 12:29 PM

Tags for this Thread