-1

I'm using an online tutorial on how to make a Text Adventure game (in VSC) and came across a really annoying issue. Even after copying all the source code from the tutorial, I receive an 'undefined reference' error. I tried editing the 'tasks.json' file as described here: undefined reference error in VScode but with no success.

The game consists of multiple c files in order to work, the source code is at the bottom of the page here: https://helderman.github.io/htpataic/htpataic05.html

Here's all the files, Thanks a lot in advance for any help!

    <main.c>

#include <stdbool.h>
#include <stdio.h>
#include "parsexec.h"


static char input[100] = "look around";

static bool getInput(void)
{
   printf("\n--> ");
   return fgets(input, sizeof input, stdin) != NULL;
}

int main()
{
   printf("Welcome to Little Cave Adventure.\n");
   while (parseAndExecute(input) && getInput());
   printf("\nBye!\n");
   return 0;
}

<parsexec.h>

   

 extern bool parseAndExecute(char *input);

    <parsexec.c>

#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "location.h"
#include "inventory.h"

bool parseAndExecute(char *input)
{
   char *verb = strtok(input, " \n");
   char *noun = strtok(NULL, " \n");
   if (verb != NULL)
   {
      if (strcmp(verb, "quit") == 0)
      {
         return false;
      }
      else if (strcmp(verb, "look") == 0)
      {
         executeLook(noun);
      }
      else if (strcmp(verb, "go") == 0)
      {
         executeGo(noun);
      }
      else if (strcmp(verb, "get") == 0)
      {
         executeGet(noun);
      }
      else if (strcmp(verb, "drop") == 0)
      {
         executeDrop(noun);
      }
      else if (strcmp(verb, "give") == 0)
      {
         executeGive(noun);
      }
      else if (strcmp(verb, "ask") == 0)
      {
         executeAsk(noun);
      }
      else if (strcmp(verb, "inventory") == 0)
      {
         executeInventory();
      }
      else
      {
         printf("I don't know how to '%s'.\n", verb);
      }
   }
   return true;
}

<location.c>

#include <stdio.h>
#include <string.h>
#include "object.h"
#include "misc.h"
#include "noun.h"

void executeLook(const char *noun)
{
   if (noun != NULL && strcmp(noun, "around") == 0)
   {
      printf("You are in %s.\n", player->location->description);
      listObjectsAtLocation(player->location);
   }
   else
   {
      printf("I don't understand what you want to see.\n");
   }
}

void executeGo(const char *noun)
{
   OBJECT *obj = getVisible("where you want to go", noun);
   if (obj == NULL)
   {
      // already handled by getVisible
   }
   else if (obj->location == NULL && obj != player->location)
   {
      printf("OK.\n");
      player->location = obj;
      executeLook("around");
   }
   else
   {
      printf("You can't get much closer than this.\n");
   }
}

  <location.h>
    extern void executeLook(const char *noun);
    extern void executeGo(const char *noun);

<object.c>
#include <stdio.h>
#include "object.h"

OBJECT objs[] = {
   {"an open field", "field"   , NULL  },
   {"a little cave", "cave"    , NULL  },
   {"a silver coin", "silver"  , field },
   {"a gold coin"  , "gold"    , cave  },
   {"a burly guard", "guard"   , field },
   {"yourself"     , "yourself", field }
};

<object.h>
typedef struct object {
   const char    *description;
   const char    *tag;
   struct object *location;
} OBJECT;

extern OBJECT objs[];

#define field      (objs + 0)
#define cave       (objs + 1)
#define silver     (objs + 2)
#define gold       (objs + 3)
#define guard      (objs + 4)
#define player     (objs + 5)

#define endOfObjs  (objs + 6)

<noun.c>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "object.h"

static bool objectHasTag(OBJECT *obj, const char *noun)
{
   return noun != NULL && *noun != '\0' && strcmp(noun, obj->tag) == 0;
}

static OBJECT *getObject(const char *noun)
{
   OBJECT *obj, *res = NULL;
   for (obj = objs; obj < endOfObjs; obj++)
   {
      if (objectHasTag(obj, noun))
      {
         res = obj;
      }
   }
   return res;
}

OBJECT *getVisible(const char *intention, const char *noun)
{
   OBJECT *obj = getObject(noun);
   if (obj == NULL)
   {
      printf("I don't understand %s.\n", intention);
   }
   else if (!(obj == player ||
              obj == player->location ||
              obj->location == player ||
              obj->location == player->location ||
              obj->location == NULL ||
              obj->location->location == player ||
              obj->location->location == player->location))
   {
      printf("You don't see any %s here.\n", noun);
      obj = NULL;
   }
   return obj;
}

OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun)
{
   OBJECT *obj = NULL;
   if (from == NULL)
   {
      printf("I don't understand who you want to %s.\n", verb);
   }
   else if ((obj = getObject(noun)) == NULL)
   {
      printf("I don't understand what you want to %s.\n", verb);
   }
   else if (obj == from)
   {
      printf("You should not be doing that to %s.\n", obj->description);
      obj = NULL;
   }
   else if (obj->location != from)
   {
      if (from == player)
      {
         printf("You are not holding any %s.\n", noun);
      }
      else
      {
         printf("There appears to be no %s you can get from %s.\n",
                noun, from->description);
      }
      obj = NULL;
   }
   return obj;
}

<noun.h>
extern OBJECT *getVisible(const char *intention, const char *noun);
extern OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun);

<inventory.c>
#include <stdio.h>
#include "object.h"
#include "misc.h"
#include "noun.h"
#include "move.h"

void executeGet(const char *noun)
{
   OBJECT *obj = getVisible("what you want to get", noun);
   if (obj == NULL)
   {
      // already handled by getVisible
   }
   else if (obj == player)
   {
      printf("You should not be doing that to yourself.\n");
   }
   else if (obj->location == player)
   {
      printf("You already have %s.\n", obj->description);
   }
   else if (obj->location == guard)
   {
      printf("You should ask %s nicely.\n", obj->location->description);
   }
   else
   {
      moveObject(obj, player);
   }
}

void executeDrop(const char *noun)
{
   moveObject(getPossession(player, "drop", noun), player->location);
}

void executeAsk(const char *noun)
{
   moveObject(getPossession(actorHere(), "ask", noun), player);
}

void executeGive(const char *noun)
{
   moveObject(getPossession(player, "give", noun), actorHere());
}

void executeInventory(void)
{
   if (listObjectsAtLocation(player) == 0)
   {
      printf("You are empty-handed.\n");
   }
}

<inventory.h>
extern void executeGet(const char *noun);
extern void executeDrop(const char *noun);
extern void executeAsk(const char *noun);
extern void executeGive(const char *noun);
extern void executeInventory(void);

<misc.c>
#include <stdio.h>
#include "object.h"

OBJECT *actorHere(void)
{
   OBJECT *obj;
   for (obj = objs; obj < endOfObjs; obj++)
   {
      if (obj->location == player->location && obj == guard)
      {
         return obj;
      }
   }
   return NULL;
}

int listObjectsAtLocation(OBJECT *location)
{
   int count = 0;
   OBJECT *obj;
   for (obj = objs; obj < endOfObjs; obj++)
   {
      if (obj != player && obj->location == location)
      {
         if (count++ == 0)
         {
            printf("You see:\n");
         }
         printf("%s\n", obj->description);
      }
   }
   return count;
}

<misc.h>

    extern OBJECT *actorHere(void);
    extern int listObjectsAtLocation(OBJECT *location);

<move.c>
#include <stdio.h>
#include "object.h"

static void describeMove(OBJECT *obj, OBJECT *to)
{
   if (to == player->location)
   {
      printf("You drop %s.\n", obj->description);
   }
   else if (to != player)
   {
      printf(to == guard ? "You give %s to %s.\n" : "You put %s in %s.\n",
             obj->description, to->description);
   }
   else if (obj->location == player->location)
   {
      printf("You pick up %s.\n", obj->description);
   }
   else
   {
      printf("You get %s from %s.\n",
             obj->description, obj->location->description);
   }
}

void moveObject(OBJECT *obj, OBJECT *to)
{
   if (obj == NULL)
   {
      // already handled by getVisible or getPossession
   }
   else if (to == NULL)
   {
      printf("There is nobody here to give that to.\n");
   }
   else if (obj->location == NULL)
   {
      printf("That is way too heavy.\n");
   }
   else
   {
      describeMove(obj, to);
      obj->location = to;
   }
}

<move.h>
extern void moveObject(OBJECT *obj, OBJECT *to);

Image of the issue in VSC: https://i.stack.imgur.com/5mkzV.png

Allan Wind
  • 23,068
  • 5
  • 28
  • 38
  • 2
    How are you compiling? – Daniel A. White Oct 14 '22 at 01:23
  • And why did you make it extern? – Daniel A. White Oct 14 '22 at 01:23
  • I've used a tutorial to setup my VSC using msys64 and mingw64, It is not my code, it's directly from a tutorial I linked, so I don't know why extern is used. – Bananatoid Oct 14 '22 at 01:28
  • `extern` is correct for functions defined in another file. People sometimes omit it, since it's implied in most contexts, but that is in fact the correct storage class. – Tom Karzes Oct 14 '22 at 01:53
  • In any case, the problem is you're trying to compile and link `main.c` by itself, which won't work since it depends on things in `parsexec.c`. You need to compile both `main.c` and `parsexec.c`, then link with both to produce your executable (or you could compile and link all at once, but usually people compile multi-file programs independently from linking). – Tom Karzes Oct 14 '22 at 01:55
  • @TomKarzes I tried changing the tasks.json file and adding the other files as args and also changing "cd "c:\Users\Christoph\Desktop\Programming\VSC-C-Inventory\" ; if ($?) { gcc main.c -o main } ; if ($?) { .\main }" to "cd "c:\Users\Christoph\Desktop\Programming\VSC-C-Inventory\" ; if ($?) { gcc main.c parsexec.h -o main } ; if ($?) { .\main }" But it has not helped – Bananatoid Oct 14 '22 at 02:22
  • `parsexec.h` should be `parsexec.c`. You should have gotten a compiler error. See my answer below on you to build. Update your question with tasks.json or whatever the file is that tells you how to build stuff. Tag question with your IDE. – Allan Wind Oct 14 '22 at 02:35
  • UPDATE I tested the files again on a different machine and it was faulty compiler or VSC settings because the program ran fine there. – Bananatoid Oct 14 '22 at 18:41

2 Answers2

0

Your code doesn't compile as it relies on header files you haven't shared with us, and executeAsk() for which we have no implementation. I removed the missing bits from parsexec.c:

#include <stdbool.h>
#include <stdio.h>
#include <string.h>

bool parseAndExecute(char *input) {
    char *verb = strtok(input, " \n");
    char *noun = strtok(NULL, " \n");
    if (verb != NULL) {
        printf("I don't know how to '%s'.\n", verb);
    }
    return true;
}

and was then able to build and execute your game:

gcc main.c parsexec.c -o game && ./game
Welcome to Little Cave Adventure.
I don't know how to 'look'.

--> look
I don't know how to 'look'.

--> ^C

This tells me it's a build issue. Specifically, you are probably not linking parsexec.o to your binary.

Allan Wind
  • 23,068
  • 5
  • 28
  • 38
  • I have not shared all the code as I thought it was okay to post a link to the entire source code. I'll edit my post and add all the code. – Bananatoid Oct 14 '22 at 02:24
  • No, you should provide a [mre] and in this case main piece of data we are missing is how you build your program. Code is fine. – Allan Wind Oct 14 '22 at 02:28
  • thing is, this is not my code, it's from a tutorial so I'm not sure how to tell you guys how it's built – Bananatoid Oct 14 '22 at 02:35
  • "thing is, this is not my code" tells me that you don't want to put any effort into this, so I certainly don't want to go out of my way to help you. If you don't know how the build system works in visual studio code that would a great thing to tell us in a comment when we ask for that information. I also tagged the question with your ide. I never used it, so I will not be particular helpful. – Allan Wind Oct 14 '22 at 02:44
  • I'm sorry if that seemed like I did not put the effort in, I'm a total rookie and I had no idea how to begin explaining how the program was built. – Bananatoid Oct 14 '22 at 02:49
  • We are happy to help especially rookies. At this point my my suggestion would be to get rid of but the 3 files you shared initially but change parsexec.c to what I showed you in my answer. Include a copy of your current tasks.json file. Then rewrite the question asking for help how to build your program. – Allan Wind Oct 14 '22 at 02:56
  • `--> compile this` ... `:-)` – Fe2O3 Oct 14 '22 at 03:08
  • Double compile it in order to improve performance :-) – Allan Wind Oct 14 '22 at 03:33
0

It turned out to be a faulty compiler/settings in VSC as the code ran fine on a different machine.

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Oct 19 '22 at 01:23