-1
/**
 * fifteen.c
 *
 * Computer Science 50
 * Problem Set 3
 *
 * Implements Game of Fifteen (generalized to d x d).
 *
 * Usage: fifteen d
 *
 * whereby the board's dimensions are to be d x d,
 * where d must be in [DIM_MIN,DIM_MAX]
 *
 * Note that usleep is obsolete, but it offers more granularity than
 * sleep and is simpler to use than nanosleep; `man usleep` for more.
 */

#define _XOPEN_SOURCE 500

#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

// constants
#define DIM_MIN 3
#define DIM_MAX 9

// board
int board[DIM_MAX][DIM_MAX];
int b;

// dimensions
int d;

// prototypes
void clear(void);
void greet(void);
void init(void);
void draw(void);
bool move(int tile);
bool won(void);

int main(int argc, string argv[])
{
    // ensure proper usage
    if (argc != 2)
    {
        printf("Usage: fifteen d\n");
        return 1;
    }

    // ensure valid dimensions
    d = atoi(argv[1]);
    if (d < DIM_MIN || d > DIM_MAX)
    {
        printf("Board must be between %i x %i and %i x %i, inclusive.\n",
            DIM_MIN, DIM_MIN, DIM_MAX, DIM_MAX);
        return 2;
    }

    // open log
    FILE* file = fopen("log.txt", "w");
    if (file == NULL)
    {
        return 3;
    }

    // greet user with instructions
    greet();

    // initialize the board
    init();

    // accept moves until game is won
    while (true)
    {
        // clear the screen
        clear();

        // draw the current state of the board
        draw();

        // log the current state of the board (for testing)
        for (int i = 0; i < d; i++)
        {
            for (int j = 0; j < d; j++)
            {
                fprintf(file, "%i", board[i][j]);
                if (j < d - 1)
                {
                    fprintf(file, "|");
                }
            }
            fprintf(file, "\n");
        }
        fflush(file);

        // check for win
        if (won())
        {
            printf("ftw!\n");
            break;
        }

        // prompt for move
        printf("Tile to move: ");
        int tile = GetInt();

        // quit if user inputs 0 (for testing)
        if (tile == 0)
        {
            break;
        }

        // log move (for testing)
        fprintf(file, "%i\n", tile);
        fflush(file);

        // move if possible, else report illegality
        if (!move(tile))
        {
            printf("\nIllegal move.\n");
            usleep(500000);
        }

        // sleep thread for animation's sake
        usleep(500000);
    }

    // close log
    fclose(file);

    // success
    return 0;
}

/**
 * Clears screen using ANSI escape sequences.
 */
void clear(void)
{
    printf("\033[2J");
    printf("\033[%d;%dH", 0, 0);
}

/**
 * Greets player.
 */
void greet(void)
{
    clear();
    printf("WELCOME TO GAME OF FIFTEEN\n");
    usleep(2000000);
}

/**
 * Initializes the game's board with tiles numbered 1 through d*d - 1
 * (i.e., fills 2D array with values but does not actually print them).  
 */
void init(void)
{
    for(int i = 0, j = 0, k = ((d*d)-1); i < d; j++, k--)
    {
        if(j == d)
        {
            i = i + 1;
            j = 0;
        }
        board[i][j] = k;
    }
    if((d*d)%2 == 0)
    {
        board[(d-1)][(d-2)] = 2;
        board[(d-1)][(d-3)] = 1;
    }
    board[(d-1)][(d-1)] = 0;
    b = board[(d-1)][(d-1)];


}

/**
 * Prints the board in its current state.
 */
void draw(void)
{
    for(int i = 0, j = 0; i !=(d-1) || j!=d; j++)
    {
        if(j == d)
        {
          i = i + 1;
          j = 0;
          printf("\n");
         }
        if(board[i][j] == 0) //b used to be 99
        {
            printf("  _");
        }
        else
        {
            printf(" %2d", board[i][j]);
        }
    }
    printf("\n");
}

/**
 * If tile borders empty space, moves tile and returns true, else
 * returns false. 
 */
bool move(int tile)
{
    //find tile
    for(int i = 0, j = 0; i !=(d-1) || j!=d; j++)
    {
        if(j == d)
        {
          i = i + 1;
          j = 0;
        }
        if (board[i][j] == tile)
        {
            //check if tile position is in valid perimeter of blank space
            if (board[i+1][j] == b)
            {
               board[i+1][j] = tile;
               board[i][j] = 0;
               b = board[i][j];
                return true;
            }
            if (board[i-1][j] == b)
            {
               board[i-1][j] = tile;
               board[i][j] = 0;
               b = board[i][j];
                return true;
            }
            if (board[i][j+1] == b)
            {
               board[i][j+1] = tile;
               board[i][j] = 0;
               b = board[i][j];
                return true;
            }
            if (board[i][j-1] == b)
            {
               printf("%i", board[i][j-1]);
               board[i][j-1] = tile;
               board[i][j] = 0;
               b = board[i][j];
                return true;
            }
        }
    }
    return false;
}

/**
 * Returns true if game is won (i.e., board is in winning configuration), 
 * else false.
 */
bool won(void)
{
    for(int i = 0, j = 0, k = 1; i !=(d-1) || j!=d; j++)
    {
        if(j == d)
        {
          i = i + 1;
          j = 0;
        }
        if (k == (d*d)-1)
        {
            return true;
        }
        if (board[i][j] == k)
        {
            k = k + 1;
        }
    }
    return false;
}

I originally had

board[(d-1)][(d-1)] = 0;

equal to 99 along with the move function look for 99. For my problem set I was supposed to use 0. Once I changed 99 to 0, for some reason 0 is being found if board[i][j-1] even if that means board[2][-1]. Why is that allow/why does that equal 0? and how can I disable this?

KevJpg
  • 7
  • 2
  • disabling something in C is just not possible, that is the whole idea behind the language is that you better know what you are doing – AndersK Sep 07 '16 at 05:34

2 Answers2

2

You have int board[DIM_MAX][DIM_MAX]; where #define DIM_MIN 3 and the memory allocated for elements are contiguous, so typically you will access board[1][2] by using board[2][-1]. But this is undefined behavior, which allows anything to happen, and you mustn't use that.

Quote from N1570 J.2 Undefined behavior:

An array subscript is out of range, even if an object is apparently accessible with the given subscript (as in the lvalue expression a[1][7] given the declaration int a[4][5]) (6.5.6).

MikeCAT
  • 73,922
  • 11
  • 45
  • 70
2

board[2][-1]. Why is that allow

C does allow you access out of range of an array. But it is Undefined Behavior.

why does that equal 0?

By accident. It's Undefined Behavior and it could be anything.

Van Tr
  • 5,889
  • 2
  • 20
  • 44
  • Thank you. How would one prevent it accessing undefined behavior? – KevJpg Sep 07 '16 at 05:14
  • @KevJpg By learning the rules of the language and then not writing code that violates the rules. – M.M Sep 07 '16 at 05:23
  • @KevJpg compile your code with all of your compiler warnings, and hopefully this will be caught at compilation stage as opposed to runtime. Depending upon your compiler, you could try `-Wall` – NoseKnowsAll Sep 07 '16 at 05:24
  • @NoseKnowsAll Thank you for the help! I appreciate it. – KevJpg Sep 07 '16 at 05:25
  • @M.M That's what I'm trying to do... I get you've been a professional for 16 years, but how about some empathy for people just starting? – KevJpg Sep 07 '16 at 05:26
  • @KevJpg That really is the only way, it wasn't a joke or something – M.M Sep 07 '16 at 05:37
  • @M.M I understand that, but that's what I'm trying to do - learn. You learn from mistakes. That's why I'm asking for help here. – KevJpg Sep 07 '16 at 05:47
  • 3
    @KevJpg I don't think M.M was trying to be mean. By "rules of the language" we refer to C standard definition, which tells you what is undefined behaviour and what is not. You can get warnings out of doing wrong things from compiler, but the only real "cure" is to try to familiarize yourself with the standard, either through study material or through standard itself. – eis Sep 07 '16 at 05:51
  • @eis Thanks for clarifying. – KevJpg Sep 07 '16 at 06:00