-1

I have completed the code to run the box inside of game. It is called Guess the right number! which means you can select a number from between 0 to 10, if it is not right number it will show message to try again, otherwise to appears a message you are winner with choices quit or restart. When I try to use gcc to compile it but it looks like I need to create a makefile as I was told but I have never done to make makefile for a single file or a second file would be called answer?

I have been doing C programming for 4 months so far. My program name is game.c to create Game (./Game) to start a program (game). How can I write a makefile for this?

I have no idea what is "curses" so I wanted to learn how to use with curses (<ncurses.h>) for myself when I did a lot of research so far I don't see the different betweens curses and ncurses. I have created the compile error after I use gcc in the below.

Snippet:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ncurses.h>
#include <time.h>

// PREDEFINED VALUES FOR DEFINING NON CHANGING VALUES IN CODE THIS CASE
#define WINDOWHEIGHT 20
#define WINDOWWIDTH 60
#define WINDOWSTARTX 0
#define WINDOWSTARTY 0
#define CORRECT 1
#define INCORRECT 0
#define START 2
#define WRONGFORMAT 3
#define MAXVALUE 10//You may change MAXVALUE to any num, i.e. `100` = (0-100).
#define MINVALUE 0
// PREDEFINED VALUES FOR DEFINING NON CHANGING VALUES IN THIS CASE


// initialising global structure for saving amount of right and wrong guesses and number to compare with.
struct game {
        int rightGuesses;
        int wrongGuesses;
        int rightNumber;
} myGame;

void initializeGame()
{

        // Returns a pseudo-random integer between 0 and MAXVALUE.
        int randomNumber = rand() % MAXVALUE;
        myGame.rightGuesses=0;
        myGame.rightNumber=randomNumber;
}

WINDOW *create_newwin(int height, int width, int starty, int startx)
{
        WINDOW *local_win;
        local_win = newwin(height, width, starty, startx);
        box(local_win, 0, 0);
        wrefresh(local_win);
        return local_win;
}


int getGuess()
{
        int guess=0;
        char guessString[32];
        scanf("%s", guessString);

        // Read number as string by using scanf, but convert to int for comparison with atoi()
        guess= atoi(guessString);
        size_t allowedEntries = strspn(guessString, "0123456789");

        // Some checking if guess was between allowed range + its a number + checking if answer is correct or not
        if(guess>=MINVALUE && guess<=MAXVALUE && guessString[allowedEntries] == '\0')
        {
                if(guess==myGame.rightNumber)
                        return CORRECT;
                else
                        return INCORRECT;
        }
        else
                return WRONGFORMAT;

}

/**
    Function for updating views regarding the input values...
 **/

void updateWindowTexts(WINDOW* window, int state)
{

        char* greetingsString = "Guess the correct number!";
        char* instructionsString = "Enter number 0-10 and press enter";
        char* correctGuess = "That was correct! Lets play again";
        char* incorrectGuess = "Sorry that was not right";
        char* wrongFormat = "incorrect number, please enter number between 0-10";
        char* correctAnswersString = "Correct answers:";
        char correctAnswers[32];
        char wrongAnswers[32];
        const char rightAnswersBase[] = "Right numbers so far: ";
        sprintf(correctAnswers, "%s%d", rightAnswersBase, myGame.rightGuesses);
        const char wrongAnswersBase[] = "Wrong numbers so far: ";
        sprintf(wrongAnswers, "%s%d", wrongAnswersBase, myGame.wrongGuesses);


        wclear(window);
        box (window, 0, 0);

        mvwprintw (window, 1, (WINDOWWIDTH/2)-(strlen(greetingsString)/2), greetingsString);
        mvwprintw (window, (WINDOWHEIGHT-3), (WINDOWWIDTH/2)-(strlen(correctAnswers)/2), correctAnswers);
        mvwprintw (window, (WINDOWHEIGHT-2), (WINDOWWIDTH/2)-(strlen(wrongAnswers)/2), wrongAnswers);
        mvwprintw (window, (WINDOWHEIGHT/2), (WINDOWWIDTH/2)-(strlen(instructionsString)/2), instructionsString);


        switch (state) {
        case START:
                break;
        case CORRECT:
                mvwprintw (window, WINDOWHEIGHT-5, (WINDOWWIDTH/2)-(strlen(correctGuess)/2), correctGuess);
                myGame.rightGuesses++;
                break;
        case INCORRECT:
                mvwprintw (window, (WINDOWHEIGHT-5), (WINDOWWIDTH/2)-(strlen(incorrectGuess)/2), incorrectGuess);
                myGame.wrongGuesses++;
                break;
        case WRONGFORMAT:
                mvwprintw (window, (WINDOWHEIGHT-5), (WINDOWWIDTH/2)-(strlen(wrongFormat)/2), wrongFormat);
                break;

        }
        wrefresh (window);

}

int main (int argc, char **argv)
{
        WINDOW *my_win;
        initscr();
        // Here we call crea_newwin to make new window, paremeters are static and defined at the top of file
        // You can try to play with these numbers
        my_win = create_newwin(WINDOWHEIGHT, WINDOWWIDTH, WINDOWSTARTY, WINDOWSTARTX);
        // Initialization of random generator, should only be called once.
        srand(time(NULL));

        initializeGame();
        // Update window once before enteringing loop
        updateWindowTexts(my_win,START);
        while(1)
        {
                updateWindowTexts(my_win,getGuess());
        }
        return 0;
}

gcc version:

/u1/stuff/C/projectFinal> gcc game.c
/usr/bin/ld: /tmp/ccOy8YhK.o: in function `create_newwin':
game.c:(.text+0x73): undefined reference to `newwin'
/usr/bin/ld: game.c:(.text+0xa8): undefined reference to `wborder'
/usr/bin/ld: game.c:(.text+0xb8): undefined reference to `wrefresh'
/usr/bin/ld: /tmp/ccOy8YhK.o: in function `updateWindowTexts':
game.c:(.text+0x251): undefined reference to `wclear'
/usr/bin/ld: game.c:(.text+0x285): undefined reference to `wborder'
/usr/bin/ld: game.c:(.text+0x2c5): undefined reference to `mvwprintw'
/usr/bin/ld: game.c:(.text+0x301): undefined reference to `mvwprintw'
/usr/bin/ld: game.c:(.text+0x33d): undefined reference to `mvwprintw'
/usr/bin/ld: game.c:(.text+0x379): undefined reference to `mvwprintw'
/usr/bin/ld: game.c:(.text+0x3f4): undefined reference to `mvwprintw'
/usr/bin/ld: /tmp/ccOy8YhK.o:game.c:(.text+0x444): more undefined references to `mvwprintw' follow
/usr/bin/ld: /tmp/ccOy8YhK.o: in function `updateWindowTexts':
game.c:(.text+0x4a3): undefined reference to `wrefresh'
/usr/bin/ld: /tmp/ccOy8YhK.o: in function `main':
game.c:(.text+0x4ba): undefined reference to `initscr'
collect2: error: ld returned 1 exit status
halfer
  • 19,824
  • 17
  • 99
  • 186
Mike ODell
  • 37
  • 9
  • _Can anyone help to create a makefile for me so I can look at it to learn?_ Yesss: [google "linux makefile tutorial"](https://www.google.com/search?q=linux+makefile+tutorial). I'm not sure whether you are really on Linux but for a basic makefile it probably doesn't count. – Scheff's Cat Dec 07 '18 at 06:29
  • 1
    Your build command `gcc game.c` looks a bit incomplete. I would recommend `-o Game` to force linking to output `Game` and probably some `-L` and `-l` arguments are missing. However, this can be found by a short web research as well. – Scheff's Cat Dec 07 '18 at 06:32
  • What? Show me what are you trying to say? – Mike ODell Dec 07 '18 at 07:07
  • yes I am on the Linux KDE System – Mike ODell Dec 07 '18 at 07:08
  • Concerning compiling your `game.c`: Shouldn't it be at least `gcc -o Game game.cc` to compile `game.c` to binary `Game`? Concerning your undefined reference errors, you have to link against the proper lib. [google "undefined reference curses"](https://www.google.com/search?q=undefined+reference+ncurses) brought me at once to [SO: Undefined reference when using ncurses on linux](https://stackoverflow.com/a/805347/7478597). So, it should probably be `gcc -o Game game.cc -lncurses` or `gcc -o Game game.cc -lncurses`. – Scheff's Cat Dec 07 '18 at 07:52
  • If `libncurses`/`libcurses` is not in one of the default include dir's add `-L` with the proper directory where it is located. However, I believe `-L` is actually not needed. Try `find /usr/lib -name "lib*curses*"` to find out whether I'm right. (I must admit that I've never used curses nor am I working on Linux in the last years.) – Scheff's Cat Dec 07 '18 at 07:54
  • 1
    Oops, what a nonsense: In the pre-previous comment it should've been: `gcc -o Game game.cc -lncurses` or `gcc -o Game game.cc -lcurses`. There are two of them but I don't know why. I guess, in your case `-lncurses` (considering that you did `#include `). -- I just realized that you might not be familiar with the library concept in general: [Library (computing)](https://en.wikipedia.org/wiki/Library_(computing)). – Scheff's Cat Dec 07 '18 at 08:19

1 Answers1

3

First of all you cannot create a Makefile for something if you do not know how to make it manually. So let's first fix your compilation and linking problems. In order to compile your program type:

gcc -c game.c

The -c option tells gcc that you just want to compile, not link. This command produces an object file named game.o. To automate this with make you don't need anything: make knows already how to do this. Without any Makefile, just type:

make game.o CC=gcc

and make will do the job. Note that we tell make which compiler to use by passing it on the command line a value for its standard make variable CC.

Next we want to link all object files of our project (only game.o in our case, but we could have several, corresponding to several different source files) and generate the executable. The important thing to understand here is that you are using a library of already existing functions (ncurses) that is not linked by default with any executable, because most programs do not use it. You must tell gcc to link your object file(s) with this library using the -lncurses option:

gcc game.o -o game -lncurses

Note that in a very simple example like this you can compile and link in one single call to gcc:

gcc game.c -o game -lncurses

And there again, make knows already how to do all this. It just needs to be passed the -lncurses linking option thanks to the other standard make variable LDLIBS:

make game CC=gcc LDLIBS=-lncurses

And that's it, you should be able to play your game. If you want to handle all the details yourself in a real Makefile the following should be OK:

game: game.c
    gcc game.c -o game -lncurses

But a much more make-ish solution would rather be:

CC     := gcc
LDLIBS := -lncurses

game: game.c
    $(CC) $^ -o $@ $(LDLIBS)

In order to understand this you will have to spend some time with the GNU make manual but here is a short and minimal explanation. In both versions:

<target>: <prerequisite>
    <recipe>

tells make that to build <target> it needs to have <prerequisite> and run <recipe>. It also tells make that if <target> is newer than <prerequisite> there is no need to re-build <target>. In the first version above, with:

game: game.c
    gcc game.c -o game -lncurses

make knows that:

  • if game.c does not exist it cannot build game; if it as asked to do so, it will raise an error
  • if game exists and is newer than game.c there is nothing to do to build game
  • if game does not exists or if it is older than game.c, it must run:

    gcc game.c -o game -lncurses
    

In the second version:

VARNAME := <value>

is the make syntax for setting a make variable named VARNAME to value <value>, while $(VARNAME) is the make syntax to get the value of make variable VARNAME. $@ and $^ are two automatic variables which values are, respectively, the target and the list of all prerequisites of the rule in the recipe of which they appear.

Renaud Pacalet
  • 25,260
  • 3
  • 34
  • 51
  • This is an excellent and very generous answer, given in response for what seems to be a request for free work. I add this note to encourage you to keep contributing, but also to encourage the question author to give things a go for themselves - it is the best way to learn. – halfer Dec 08 '18 at 12:39