0

Trying to make 2048 in c++ and i just added a function in Game.cpp that checks gamestate of the 4x4 grid and either shows the game is still in play or ends based on win or loss,

not sure what is going on, but i get this error from the compiler about multiple declarations or multiple definitions but im so confused about where the error is, if someone could either solve it, or explain how to understand the compilers error so im able to solve it myself ( aka where the error is, what lines its pointing to etc ) i would be grateful.

file for compiling is at the bottom ( it was provided by my uni )

This is the error in console

compiling main.cpp
In file included from main.cpp:15:
In file included from ./Grid.h:4:
In file included from ../../include/graphics.h:28:
In file included from ../../include/graphics_internal.h:18:
In file included from ../../include/juce/JuceHeader.h:23:
In file included from ../../3rdparty/JUCE/modules/juce_cryptography/juce_cryptography.h:60:
../../3rdparty/JUCE/modules/juce_cryptography/encryption/juce_BlowFish.h:100:10: warning: 
      mangled name of 'apply' will change in C++17 due to non-throwing exception
      specification in function signature [-Wc++17-compat-mangling]
    bool apply (void*, size_t, void (BlowFish::*op) (uint32&, uint32&) const noexc...
         ^
1 warning generated.
linking build/main.o
build/Game.o:(.bss+0x4): multiple definition of `grid'
build/main.o:(.bss+0x4): first defined here
clang-5.0: error: linker command failed with exit code 1 (use -v to see invocation)
Makefile:33: recipe for target 'build/2048' failed
make: *** [build/2048] Error 1

and these are my files

Main.cpp

/**
 * File: main.cpp
 * Author: nice try ;)
 * Date: 20-11-2019
 * Desc:
 * Copyright: University of West of England 2017
 */
#include <stdio.h>
#include <cstdio>
#include <stdlib.h>
// include the UFCFGL301's standard library
#include <ufcfgl-30-1.h>
#include <iostream>
#include <time.h>
#include "Grid.h"
#include "Game.h"
// uncomment if you want to use the graphics library
#include <graphics.h>


using namespace std;
using namespace uwe;

/**
 * main routine, implements ...
 * @return      success
 */
int main(void) {
    int c = 0;

    srand(time(0));

    Grid grid = Grid();
    Game game = Game();

    cout << "\033[2J"; // this clears the terminal
    cout << "Player Score : " << grid.totalScore << "\n";

    grid.addValue();
    grid.drawGrid();

    initialiseGraphics(600,400);

    loop (
        [&](){},

        [&](keyPress keyPress){
// w a s d are also inputted into an integer as their respective ascii value
// so this allows the use of w a s d and also the arrow keys in controlling
// the moves of the player
            int kpVal = keyPress.getKeyCode();

            if ( kpVal == 'q' ){
                return true;
            }
            if ( kpVal == 'w' || kpVal == keyPress::upKey){
                grid.shiftUp();
                grid.setScore(grid.score);
                cout << "\033[2J"; // this clears the terminal
                cout << "Player Score : " << grid.totalScore << "\n";
                grid.drawGrid();
                game.checkGameState();


            }
            if ( kpVal == 'a' || kpVal == keyPress::leftKey){
                grid.shiftLeft();
                grid.setScore(grid.score);
                cout << "\033[2J";
                cout << "Player Score : " << grid.totalScore << "\n";
                grid.drawGrid();
                game.checkGameState();

            }
            if ( kpVal == 's' || kpVal == keyPress::downKey){
                grid.shiftDown();
                grid.setScore(grid.score);
                cout << "\033[2J";
                cout << "Player Score : " << grid.totalScore << "\n";
                grid.drawGrid();
                game.checkGameState();

            }
            if ( kpVal == 'd' || kpVal == keyPress::rightKey){
                grid.shiftRight();
                grid.setScore(grid.score);
                cout << "\033[2J";
                cout << "Player Score : " << grid.totalScore << "\n";
                grid.drawGrid();
                game.checkGameState();

            }

        return false;
        }
    );

}

Grid.cpp

#include "Grid.h"
#include <iostream>
#include <cstdio>
#include <string>
#include <graphics.h>

// this empties the board from left to right and top to bottom
// and sets each value to 0
Grid::Grid(){
    for(int x = 0; x < tilesWide; x++){
        for(int y = 0; y < tilesHigh; y++){
            grid[x][y] = emptyCell;
            didMove = true;
        }
    }
}
// draws the current grid with lines inbetween ( ascii 4x4 grid essentially )
void Grid::drawGrid(){
    std::cout << "--------------------" << '\n';
    for(int y = 0; y < tilesWide ; y++){
        for(int x = 0;x < tilesHigh; x++){
            std::cout << "| " << grid[x][y] << " |";
        }
        std::cout << '\n' << "--------------------" << '\n';
    }
}

// function to set the score

void Grid::setScore(int score){
    totalScore = totalScore + score;
}

// this function adds a 2 or a 4 to a random empty tile

void Grid::addValue(){

    int value = Grid::randValue();

    while(true){
        int valX = rand() % tilesWide;
        int valY = rand() % tilesHigh;

        if (grid[valX][valY] == 0) {
// this if statement checking a boolean is so that a tile is only added if
// a tile has been moved or merged, so that if you press a key and nothing
// happened, it will not add a tile causing you to choose a different direction
// to move
            if(didMove == true){
                grid[valX][valY] = value;
                didMove = false;
                break;
            }
            else{
                printf("DID NOT MOVE \n");
                break;
            }
        }
    }
}

// this function generates a 2 or a 4 and returns it to the addValue function

int Grid::randValue(){
    int randVal = rand() % 10;
    // 90% chance of spawning a 2, same probability as the original source code
    if(randVal < 9){
        return 2;
    }
    else{
        return 4;
    }
}

// this function is so that the value of grid[x][y] can be checked in another 
// source file ( in this case in game.cpp for game state check) as using
// grid[x][y] was not recognised

int Grid::gridValue(int x, int y){
    return grid[x][y];
}

//-----------------------------TILE MOVEMENT----------------------------------//

// this packs and merges all the tiles up to the top of the board if there is 
// space
// up // down // left // right // order of functions 

void Grid::shiftUp(){
    moveUp();
    mergeUp();
    moveUp();
    addValue();
}

void Grid::shiftDown(){
    moveDown();
    mergeDown();
    moveDown();
    addValue();
}

void Grid::shiftLeft(){
    moveLeft();
    mergeLeft();
    moveLeft();
    addValue();
}

void Grid::shiftRight(){
    moveRight();
    mergeRight();
    moveRight();
    addValue();
}

// functions to move and merge upwards

void Grid::moveUp(){
    for(int x = 0; x < tilesWide; x++){
        for(int y = 0; y < tilesHigh; y++){
            if( grid[x][y] == emptyCell){

                for(int t = y+1; t < tilesWide; t++){
                    if(grid[x][t] != emptyCell){  

                        grid[x][y] = grid[x][t];
                        grid[x][t] = emptyCell;
                        didMove = true;
                        break;
                    }
                }
            }
        }
    }
}

void Grid::mergeUp(){
    for(int x = 0; x < tilesWide; x++){
        for(int y = 0; y < tilesHigh-1; y++){

            if(grid[x][y] == grid[x][y+1]){
                grid[x][y] = ( grid[x][y] *2);
                grid[x][y+1] = emptyCell;
                setScore(grid[x][y]);
                didMove = true;
            }
        }
    }  
}

// functions to move and merge downwards

void Grid::moveDown(){
    for(int x = 0; x < tilesWide; x++){
        for(int y = tilesHigh - 1; y > 0 ; y--){
            if( grid[x][y] == emptyCell){

                for(int t = y-1; t >= 0; t--){
                    if(grid[x][t] != emptyCell){  

                        grid[x][y] = grid[x][t];
                        grid[x][t] = emptyCell;
                        didMove = true;
                        break;
                    }
                }
            }
        }
    }
}

void Grid::mergeDown(){
    for(int x = 0; x < tilesWide; x++){
        for(int y = tilesHigh-1; y > 0; y--){

            if(grid[x][y] == grid[x][y-1]){
                grid[x][y] = (grid[x][y] *2);
                grid[x][y-1] = emptyCell;
                setScore(grid[x][y]);
                didMove = true;
            }
        }
    }  
}

// functions to move and merge to the left

void Grid::moveLeft(){
    for(int x = 0; x < tilesWide; x++){
        for(int y = 0; y < tilesHigh; y++){
            if( grid[x][y] == emptyCell){

                for(int t = x+1; t < tilesHigh; t++){
                    if(grid[t][y] != emptyCell){  

                        grid[x][y] = grid[t][y];
                        grid[t][y] = emptyCell;
                        didMove = true;
                        break;
                    }
                }
            }
        }
    }
}

void Grid::mergeLeft(){
    for(int x = 0; x < tilesWide-1; x++){
        for(int y = 0; y < tilesHigh; y++){

            if(grid[x][y] == grid[x+1][y]){
                grid[x][y] = (grid[x][y] *2);
                grid[x+1][y] = emptyCell;
                setScore(grid[x][y]);
                didMove = true;
            }
        }
    }  
}

// functions to move and merge to the right

void Grid::moveRight(){
    for(int x = tilesWide - 1; x >= 0; x--){
        for(int y = 0; y < tilesHigh; y++){
            if( grid[x][y] == emptyCell){

                for(int t = x-1; t >= 0; t--){
                    if(grid[t][y] != emptyCell){  

                        grid[x][y] = grid[t][y];
                        grid[t][y] = emptyCell;
                        didMove = true;
                        break;
                    }
                }
            }
        }
    }
}

void Grid::mergeRight(){
    for(int x = tilesWide - 1; x > 0; x--){
        for(int y = 0; y < tilesHigh; y++){

            if(grid[x][y] == grid[x-1][y]){
                grid[x][y] = (grid[x][y] *2);
                grid[x-1][y] = emptyCell;
                setScore(grid[x][y]);
                didMove = true;
            }
        }
    }
}

Grid.h

#pragma once
#include <cstdio>
#include <string>
#include <graphics.h>

class Grid {
public:
    Grid();
// defining functions for processing user input into various moves on the board
    void drawGrid();

    int randValue();
    void addValue();

    void moveLeft();
    void mergeLeft();

    void moveRight();
    void mergeRight(); 

    void moveUp();
    void mergeUp();

    void moveDown();
    void mergeDown();

    void shiftUp();
    void shiftDown();
    void shiftLeft();
    void shiftRight();

    int gridValue(int x, int y);

    static const int tilesWide = 4;
    static const int tilesHigh = 4;

    bool didMove;

    int grid[tilesWide][tilesHigh];

    int score = 0;
    int totalScore = 0;
    void setScore(int score);

    static const int emptyCell = 0;

private :




};

Game.cpp

#include "Game.h"
#include <iostream>
#include <cstdio>
#include <string>
#include <graphics.h>

Game::Game(){}

bool canMove(){
    bool canMove = false;
    for(int x = 0; x < Grid::tilesWide; x++){
        for(int y = 0; y < Grid::tilesHigh; x++){

            if (grid.gridValue(x,y) == Grid::emptyCell) {

                canMove = true;
                return canMove;
            }
            else if(grid.gridValue(x,y) == grid.gridValue(x,y+1) ||
                    grid.gridValue(x,y) == grid.gridValue(x+1,y) ) {

                canMove = true;
                return canMove;
             }
        }
    }
    return canMove;
}

bool checkWin(){
    bool win = false;
    for(int x = 0; x < Grid::tilesWide; x++){
        for(int y = 0; y < Grid::tilesHigh; x++){
            if(grid.gridValue(x,y) == 2048){
                win = true;
            }
        }
    }
    return win;
}

bool checkLoss(){
    bool loss = false;
    if(canMove() == false ){
        loss = true;
    }
    return loss;
}

void checkGameState(){
    if(checkWin() == true) {
        std::cout << "YOU WIN";
        exit(1);
    }
    else if(checkLoss() == true) {
        std::cout << "YOU LOSE";
        exit(1);
    }
}

Game.h

#pragma once
#include "Grid.h"
#include <cstdio>
#include <string>
#include <graphics.h>


Grid grid;

class Game{

public:

    Game();

    bool canMove();
    bool checkWin();
    bool checkLoss();
    void checkGameState();


private:

};

MakeFile

# File: Makefile
# Author: again, nice try ;)
# Date: 20-11-2019
#
# Copyright: University of West of England 2017
#
ifeq ($(origin ROOTDIR), undefined)
    ROOTDIR=../..
endif
include $(ROOTDIR)/MakefileDefs.inc

# Build path
BUILD_DIR = build

TARGET = 2048
CPP_SOURCES = main.cpp Grid.cpp Game.cpp

#######################################
# build the application
#######################################
OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(CPP_SOURCES:.cpp=.o)))
vpath %.cpp $(sort $(dir $(CPP_SOURCES)))

CFLAGS += -I$(INCDIR) -I$(JUCEINSTALL)/modules

LIBS =  $(LIBDIR)/libgraphics.a $(JUCELIB) $(LIBDIR)/libufcfgl-30-1.a

$(BUILD_DIR)/%.o: %.cpp Makefile | $(BUILD_DIR)
    $(ECHO) compiling $<
    $(CC) -c $(CFLAGS) $< -o $@

$(BUILD_DIR)/$(TARGET): $(OBJECTS) $(LIBS) Makefile
    $(ECHO) linking $<
    $(CC) $(OBJECTS) $(LIBS) $(LINUXLIBFALGS) $(LDFLAGS) -o $@
    $(ECHO) success

$(BUILD_DIR):
    mkdir -p $@

#######################################
# install
#######################################
install:


#######################################
# clean up
#######################################
clean:
    -rm -fR .dep $(BUILD_DIR)

#######################################
# dependencies
#######################################
-include $(shell mkdir .dep 2>/dev/null) $(wildcard .dep/*)

.PHONY: clean all
m e m e
  • 105
  • 2
  • 11
  • not really, i need a bit more depth and relevance, im new to coding let alone c++ and i dont see the issue, it was compiling fine with no warnings before i added the checkgamestate functions in another .cpp file – m e m e Dec 29 '19 at 17:33
  • You cannot declare a variable in a header file (if it is included in more than one cpp file). Follow the solution provided in the link. You need to declare your global variable in one of the cpp files and in header file you only `extern` it. – Yksisarvinen Dec 29 '19 at 17:35
  • 1
    And anyway, why is `grid` not a member of `Game`? That would solve your problems with global variables. – Yksisarvinen Dec 29 '19 at 17:38
  • sorry for being dim, which variable are u referencing, grid isnt a variable, its a class so im not sure which variable you are referencing from my code, also the reason why i removed grid from being a member of Game, was because when i put it into either the public or private part of Game, the code in Game.cpp that involved grid ( aka grid.gridValue(x,y) ) would show up with the error of "grid is undefined" – m e m e Dec 29 '19 at 17:42
  • @meme - `Grid` is a class. `grid` very much *is* a variable (an *instance* of the `Grid` class). Note that C++ is *case sensitive* - "Grid" and "grid" are two different things. – Jesper Juhl Dec 29 '19 at 17:48
  • (Probably) 8-th line of `Game.h` is a declaration of global variable of type `Grid` called `grid` - this line: `Grid grid;`. This variable is causing your linker errors. I don't see any reason why would it cause errors, I'd have to see that error to say more. – Yksisarvinen Dec 29 '19 at 17:48
  • 1
    Also, once it compiles successfully, notice that `grid` in `main()` and this global `grid` are two distint objects. Changes in one of them will not be reflected to the other. – Yksisarvinen Dec 29 '19 at 17:49
  • im sorry but im still so confused, i understand now that the instance of type Grid is a global variable, however im still not sure what i need to do to fix this :( – m e m e Dec 29 '19 at 17:53
  • Possibly relevant: [Definitions and ODR](https://en.cppreference.com/w/cpp/language/definition). – Jesper Juhl Dec 29 '19 at 17:53
  • so if i want the functions in Game.cpp to append the instance of Grid called grid, aswell as the functions in Grid.cpp what would i need to do exactly – m e m e Dec 29 '19 at 17:55
  • 1
    Also relevant: [The Definitive C++ Book Guide and List](https://stackoverflow.com/q/388242/5910058). – Jesper Juhl Dec 29 '19 at 17:57
  • i understand header files are for declarations and source files are for defining such functions and integers etc, however in my case i dont see the issues, ive only started running into the issue when i started using another sourcefile other than main.cpp and grid.cpp, however i honestly dont know what i just did but it started compiling correctly, and the program runs, going to see if it checks the gamestate on the correct instance of Grid ( the instance i called grid ) – m e m e Dec 29 '19 at 18:00
  • i added a cout line in a function and now i get the same error as i posted at the top – m e m e Dec 29 '19 at 18:01
  • 1
    The error you ask about comes exactly from the global variable in header file. I'm afraid I cannot make it anymore clear. The solution to problem of global variable in header file is in the question I linked as duplicate (first comment). This question has a very clear explanation of how to declare a global variable that can be used in more than one cpp file. It's better to avoid global variables altogether, but it would require quite a lot of changes in your code. – Yksisarvinen Dec 29 '19 at 18:07
  • so how can i get a global instance under the type of Grid that can be accessed by 3 .cpp files – m e m e Dec 29 '19 at 19:43

0 Answers0