Koren Leslie Cohen

  • About
  • Blog
  • Contact

7 comments

C, Harvard CS50

Game of Fifteen in C

April 24, 2014 by Koren Leslie Cohen

The Game of Fifteen is a puzzle played on a square, two-dimensional board with numbered tiles that slide. The goal of this puzzle is to arrange the board’s tiles from smallest to largest, left to right, top to bottom, with an empty space in board’s bottom-right corner.

/**
 * fifteen.c
 *
 * Computer Science 50
 * Problem Set 3
 *
 * Implements the 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 [MIN,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>

// board's minimal dimension
#define MIN 3

// board's maximal dimension
#define MAX 9

// board, whereby board[i][j] represents row i and column j
int board[MAX][MAX];

// board's dimension
int d;

// initialize global variables
int tile;
int blankspace;
int x;
int y;
int i;
int j;

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

/**
 * Main function - sets up how game will operate.
 */
int main(int argc, string argv[])
{
    // greet player
    greet();

    // ensure proper usage
    if (argc != 2)
    {
        printf("Usage: ./fifteen d\n");
        return 1;
    }

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

    // initialixing x and y here so 0 does not reset every time you call move function
    else
    {
        x = (d - 1);
        y = (d - 1);
    }

    // initialize the board
    init();

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

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

        // saves the current state of the board (for testing)
        save();

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

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

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

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

    // that's all folks
    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("GAME OF FIFTEEN\n");
    usleep(2000000);
}

/**
 * Initializes the game's board with tiles numbered 1 through d*d - 1,
 * (i.e., fills board with values but does not actually print them),
 * whereby board[i][j] represents row i and column j.
 */
void init(void)
{
    // TODO
    int dsquared = (d * d);
    int n = 0;

    // two for loops to iterate through 2d array - rows/columns
    for (int i = 0; i < d; i++)
    {
        for (int j = 0; j < d; j++)
        {
            // this will initialize the array counting from largest number down to d * d - 1
            // n is used as a counter so we are subtracting by one more each time 
            n = n + 1;
            board[i][j] = (dsquared - n);
        }
    }   
    // if d is even and the tiles on board odd, swap 1 and 2 so game can be won
    if ((d % 2) == 0)
    {
        int temp = board[d - 1][d - 2];
        board[d - 1][d - 2] = board[d - 1][d - 3];
        board[d - 1][d - 3] = temp; 
    }               
}

/**
 * Prints the board in its current state.
 */
void draw(void)
{
    // TODO nested loops to draw board initialized above

    // initialize variables
    int n = 0;

    // iterate over rows
    for (int i = 0; i < d; i++)
    {
        // iterate over columns
        for (int j = 0; j < d; j++)
        {
            // subtract by one more each time
            n = (n + 1); 

            // print the number of the tile except for the 0 tile
            if (board[i][j] > 0) 
            {
                printf("| %2d ", board[i][j]);
            }

            // for 0 tile, print the space for tiles to move
            if (board[i][j] == 0) 
            {
                printf("| __ ");
            }
        }
        // print two lines after each row to make board
        printf("|\n\n"); 
    }    
}

/**
 * If tile borders empty space, moves tile and returns true, else
 * returns false. 
 */
bool move(int tile)
{
    // TODO 
    // search board for user tile
    // iterate through rows to find tile
    for (int i = 0; i < d; i++) 
    {
        // iterate through columns to find tile
        for (int j = 0; j < d; j++) 
        {
            // if user entered tile that exists on the board
            if(tile == board[i][j]) 
            {
                // initialize variable for 0 
                int blankspace = 0;       

                // test if tile is adjacent to blankspace
                if (((x == (i - 1)) && (j == y)) ||  ((x == (i + 1)) && (j == y)) ||
                ((i == x) && (y == (j - 1))) || ((i == x) && (y == (j + 1))))
                {
                    // printf("before:  tile %d, blankspace %d\n", tile, blankspace)

                    // swap tile with blankspace if tile is adjacent to blankspace
                    board[x][y] = tile;
                    board[i][j] = blankspace;
                    x=i;
                    y=j;

                    // printf("The position of the tile is board[%d][%d] = %2d.  Tile is %d\n", i, j, board[i][j], tile);
                    // printf("Blankspace position is: board[%d][%d] = %d\n", x, y, blankspace);

                    return true;
                }   
            } 
        }
    }
    return false;
}

/**
 * Returns true if game is won (i.e., board is in winning configuration), 
 * else false.
 */
bool won(void)
{
    // TODO
    // initializes variables
    int n = -1;

    // iterates through board
    for (int i = 0; i < d; i++) 
    {
        for (int j = 0; j < d; j++)
        {
            // creates counter
            n = n + 1;

            // if any tile != counter, counting from 0, return false
            if (board[i][j] != n)
            {
            return false;
            }
        } 
    }
    // otherwise, all tiles count up from 0, game won
    return true;
}

/**
 * Saves the current state of the board to disk (for testing).
 */
void save(void)
{
    // log
    const string log = "log.txt";

    // delete existing log, if any, before first save
    static bool saved = false;
    if (!saved)
    {
        unlink(log);
        saved = true;
    }

    // open log
    FILE* p = fopen(log, "a");
    if (p == NULL)
    {
        return;
    }

    // log board
    fprintf(p, "{");
    for (int i = 0; i < d; i++)
    {
        fprintf(p, "{");
        for (int j = 0; j < d; j++)
        {
            fprintf(p, "%i", board[i][j]);
            if (j < d - 1)
            {
                fprintf(p, ",");
            }
        }
        fprintf(p, "}");
        if (i < d - 1)
        {
            fprintf(p, ",");
        }
    }
    fprintf(p, "}\n");

    // close log
    fclose(p);
}

Start:

fifteen.c

FTW!:

fifteen.c

  • About
  • Latest Posts
Connect
Koren Leslie Cohen
Product manager at Facebook. Former senior product manager at Dollar Shave Club in Los Angeles and software engineer at J.Crew / Madewell in New York City. Recovering trial lawyer.
Connect
Latest posts by Koren Leslie Cohen (see all)
  • PM Career Story - April 28, 2022
  • How to Transition into Product Management - December 26, 2017
  • What I’ve Learned in My First Few Months as a Product Manager - October 14, 2015

Related Posts

Caesar Cipher in C
FizzBuzz in C

Share

Facebook Google+ Twitter Pinterest Email

Comments Cancel reply

Your email address will not be published. Required fields are marked *

*

code

  1. Comment says

    August 24, 2014 at 9:24 am

    I have a querry.
    Why did you write x and y = (d – 1)? I want to understand it. And

    In move function, you wrote x = i and y = j, why that

    Please explain me 🙂

    Reply
    • Koren Leslie Cohen says

      August 24, 2014 at 8:02 pm

      I used x and y variables for the blankspace and later for the swap.

      x and y are originally both set to d-1 to represent the blankspace. For example, if someone chose 3 for d, meaning the board has 3 rows (0, 1, 2) and 3 columns (0, 1, 2), as pictured above, the blankspace (represented by x, y) would initially be set to [2][2] on the board, or [d-1][d-1].

      In the move function, I measure whether a square is adjacent to the blankspace by measuring i and j as related to x and y, and if adjacent, swapping the tile for the blankspace.

      Reply
      • Comment says

        August 25, 2014 at 2:48 am

        Thank you 😀

        Reply
  2. EwokABdevito says

    January 30, 2016 at 3:32 pm

    Why does the init function have:

    n = n + 1;

    and the draw function have:

    n = (n+1);

    Reply
  3. Kgotso Koete says

    September 15, 2016 at 3:44 pm

    I am still a novice at programming, but I must say your code is beautiful to look at. I wish all code was written this way, and with this site’s colour scheme, I wish I could read non-fiction like this. Beautiful!

    Reply
  4. bijan says

    October 29, 2016 at 8:10 am

    Hey, i liked your code and used very similar structure myself.

    The only difference between your code and mine was that i declared the type and value of x and y in the same line. This caused the function “move” to see x and y as undeclared variables.

    Why is it that when you declare the variable type and value in different lines, that the code works?

    Reply
  5. ding says

    December 25, 2016 at 1:36 am

    actually the implementation of this game is not entirely correct, since it is stated in the pset that board only fulfills win condition if the blank is on the bottom right. (you kinda also said it yourself)
    you also initialized int n in draw() which wasn’t used! XP

    on the otherhand gr8 job on completing the pset!

    Reply

Back to Blog

  • GitHub
  • Instagram
  • LinkedIn
  • RSS
  • Twitter

Looking for something?

Copyright 2023 Koren Leslie Cohen