Why this Error? How should I fix it?

Question:
Why am I getting the traceback error? How would I define the self?
Repl link:
https://replit.com/@Pcat11/Text-Based-Game?v=1

code snippet

changes

First i moved your valid_jobs to before the input

valid_jobs = ['wizard', 'warrior', 'rogue']
player_job = input("> ").lower()
if player_job in valid_jobs:
  myPlayer.job = player_job

I also changed where you were using .lower() from the if check, to the input itself.
you had :

player_job = input("> ")
valid_jobs = ['wizard', 'warrior', 'rogue']
if player_job.lower() in valid_jobs:
   myPlayer.job = player_job

I changed it to :

valid_jobs = ['wizard', 'warrior', 'rogue']
player_job = input("> ").lower() ## may need to be changed depending on how you intend to use it
if player_job in valid_jobs:
   myPlayer.job = player_job

Then I just moved your player stats around.

I moved

if myPlayer.job == 'warrior':
      self.hp = 120
      self.mp = 20
  elif myPlayer.job == 'wizard':
      self.hp = 40
      self.mp = 120
  elif myPlayer.job == 'rogue':
      self.hp = 60
      self.mp = 60

From where you had it:

to :

if player_job in valid_jobs:
    myPlayer.job = player_job
    if myPlayer.job == 'warrior':
      self.hp = 120
      self.mp = 20
    elif myPlayer.job == 'wizard':
      self.hp = 40
      self.mp = 120
    elif myPlayer.job == 'rogue':
      self.hp = 60
      self.mp = 60

Here’s the whole modified part.

 question2 = "Hello, what role do you want to play?\n"
  question2added = "You may play as a wizard, warrior, or a rogue.\n"
  for character in question2:
    sys.stdout.write(character)
    sys.stdout.flush()
    time.sleep(0.05)
  for character in question2added:
    sys.stdout.write(character)
    sys.stdout.flush()
    time.sleep(0.05)
  valid_jobs = ['wizard', 'warrior', 'rogue']
  player_job = input("> ").lower()

  if player_job in valid_jobs:
    myPlayer.job = player_job
    if myPlayer.job == 'warrior':
      self.hp = 120
      self.mp = 20
    elif myPlayer.job == 'wizard':
      self.hp = 40
      self.mp = 120
    elif myPlayer.job == 'rogue':
      self.hp = 60
      self.mp = 60
  print("You are now a " + myPlayer.job + ".")
  while player_job.lower() not in valid_jobs:
      player_job = input("> ")
      if player_job.lower() in valid_jobs:
        myPlayer.job = player_job
        print("You are now a" + myPlayer.job + ".")

  #Player Stats

It’s still telling me there’s an undefined name error but as you can see I was able to get through the name error you were encountering after choosing a playerjob:

I used different names but the same structure to explain what was happening in the order of operations.

https://replit.com/@vvithershins/Fork-Text-Based-Game

I unmarked that post as the solution as the issue doesn’t seem to be solved.

The reason this is happening is because self is not defined, as the error says. It has nothing to do with the order of your code.

The fix is to use myPlayer. instead of self..

Or if you have used too many self in the code and doesn’t want to change every single of them, you can alternatively add self = myPlayer before every self references but after myPlayer declaration

Ctrl + H

Problem solved

(But that does change the self inside of the class though, you can hold alt for multiple cursors then)

My apologies. It was working at first to get through the barrier they were facing. But it now suddenly has stopped working.

It doesn’t seem like you replace self. everywhere?

I removed the whole myPlayer.job = player_job part from the first time it asks for the job.[wizard, warrior , rogue] and just used if player_job in valid_jobs:

but then left the while not in valid_jobs part alone.

.
I can get through the NameError: name ‘self’ is not defined part now , and move north once,then i try to move west and then get this : TypeError: can only concatenate str(not "tuple") to str.

sorry should also change line 345 so it doesn’t just say "you are now a . "

I tried this too and it worked :+1:

For the TypeError, go to line 293 and replace destination with destination[1]

1 Like

I did and then got this instead.
AttributeError : ‘tuple’ object has no attribute ‘upper’

I went to line 253 and removed the .upper() and then got this again


tried removing the .lower() from both of the player_moves as well but still just get the type error can only concatenate str (not “tuple”) to str

1 Like

Try:

def print_location():
    print('\n' + ('#' * (4 + len(str(myPlayer.location)))))
    print('# ' + str(myPlayer.location).upper() + ' #')
    print('# ' + zonemap[myPlayer.location][DESCRIPTION] + ' #')
    print('\n' + ('#' * (4 + len(str(myPlayer.location)))))
    print('#')
1 Like


1 Like

I just took another look; it’s the way you defined the movement directions in the zonemap dictionary. The values for the UP, DOWN, LEFT, and RIGHT keys are tuples containing two strings. So, when you try to access them using zonemap[myPlayer.location][UP], it returns a tuple, and attempting to concatenate a tuple with a string results in an error. To fix this, consider storing strings instead of tuples, or even lists; lists would work as well.

1 Like

This is probably why the first move works.
(The starting point)


it’s moving directly to each cell or square instead of using the tuple.

1 Like

i start at b2 i move up which moves me to a2 ,
so i changed how a2 works to reflect how b2 works
and now i can move.
I move “left” which takes me to a1
image
for the edges of the grid if they move to a nonexisting square i just put them back in the same square theyre already in. (if a1 tries to move up (or left), put them back to a1



(or you could make it wrap around and go to a4(left) or d1(up) or warp somewhere really just whichever coordinates (ex. a1or b3 or c4 ) you want

I’ve made various changes to the code and layout though so it’s best to view my repl. I’m making a copy of it right now so that if i work on it any further there’ll still be the copied version right now.

https://replit.com/@vvithershins/Fork-Text-Based-Game#main%20(copy).py

Going to try to make all of them work the same way. (in main.py)
I’m not sure how the rest of the game is supposed to work, just the player_job and movement so i don’t know if this will mess up anything the creator wanted. So please check it thoroughly to see if everything still works how you intended it to.
I think everything should be fine now though.

I’ve been wanting to learn how to do something like this so thank you for letting me contribute (if you choose to do it this way) lol

the only thing is if you dont want it to say “you have moved to the b4”

I tried using dest instead but it’s unbound.
so i moved it here instead

its a little grammatically incorrect now though ,since if they say west itll say “moved to the west” but if they say left it’ll say"moved to the left" which will sound odd sometimes.
(you have moved to the down)

I changed it to


“you have moved west.” or “you have moved left.” both sound good.

Here’s what chat gpt said about what I have now :

The code you provided seems to be a basic text-based RPG game written in Python. It appears to have a structure for player setup, a title screen, help menu, a map, and game interactivity. However, there are a few issues and improvements that can be made:

  1. Class Attribute Assignment: In the setup_game function, you’re attempting to set the player’s job and adjust the player’s stats based on the chosen job. However, there are some issues with the assignment. You’re using self instead of myPlayer, and there’s a logical error where you’re comparing myPlayer.job with different job names. It should be fixed like this:
if player_job == 'warrior':
    myPlayer.hp = 120
    myPlayer.mp = 20
elif player_job == 'wizard':
    myPlayer.hp = 40
    myPlayer.mp = 120
elif player_job == 'rogue':
    myPlayer.hp = 60
    myPlayer.mp = 60
  1. Input Validation: It’s generally a good practice to validate user inputs to avoid unexpected errors. For example, in the title_screen_selections function, you could convert the input to lowercase once and use that value throughout the function. This way, you don’t need to call lower() multiple times.
option = input("> ").lower()
  1. Redundant Code: The title_screen_selections function has some redundant code. You can simplify it by using a loop to repeatedly ask for input until a valid option is provided.
while option not in ['play', 'help', 'quit']:
    print("Invalid Option, Enter Valid Command")
    option = input("> ").lower()
  1. Unused Variables: The variables screen_width and solved_places are defined but not used in the provided code. #I assume theyre working on that
  2. Consistent Naming: It’s a good practice to use consistent naming conventions. For example, you have title_screen and title_screen_selections functions. It might be clearer to name them consistently, such as display_title_screen and get_title_screen_input.
  3. Inconsistent Indentation: The indentation for the class definition player is inconsistent. Make sure it aligns properly.
  4. Unused Imports: The import os statement is present, but the os module is not used in the provided code. You can remove it unless you plan to use it later.
  5. Clear Screen Function: The os.system('clear') statements are used for clearing the screen. This may work on Unix-based systems, but it might not work on all platforms. Consider using a more cross-platform solution if you anticipate running the game on different systems.
  6. Function Naming: The function names are generally descriptive, but you might want to make sure they follow a consistent naming convention (e.g., snake_case).

With these suggestions, your code should be more readable and less error-prone.
https://replit.com/@vvithershins/Fork-Text-Based-Game#airesponse.jpg


I think ( I could be mistaken) that the problem was that you assigned the stats under the while not in valid jobs (re ask for job) part

so if they entered a valid job the first time , it would not assign them their job or stats correctly.
But then you also didnt have it in line with the secondif player_job.lower in valid_jobs:part (after the while not in valid jobs) either so it would only assign the stats while it was not a valid job, and since stats are only assigned TO valid jobs , it would be impossible to assign stats.

player_job = input("wizard, warrior or rogue")
valid_jobs = ['wizard', 'warrior', 'rogue']
⬇️if player_job in valid _jobs:                                        1🔗 
🛑   myPlayer.job = player_job                                          🛑
⬇️while job not in valid_jobs:                                         1🖇️
       player_job = input("wizard, warrior or rogue")                    | \
       if player_job in valid _jobs:                                     |  2🔗 
        myPlayer.job = player_job                                        |
                                                                         |
⬆️if player_job is one of the valid_jobs:                              2🖇️
     assign stats

:link:1 tries to reach :paperclips:2 but is interrupted by :paperclips:1, so the chain is broken
(when :paperclips:1 and :paperclips:2 are linked 2 :paperclips: is impossible)
:link:2 is not properly indented and is not part of the while loop scope :robot:
I tried testing for this but couldnt seem to get any hint of if that was the case or not.

This is why i moved your stats to after the first time it asks for the job [wizard, warrior, rogue]

@Pcat11
:robot:Yes, you’ve correctly identified the issue. The while loop condition is checking whether the job input is not in the valid jobs list, and within the loop, you’re re-assigning the job based on user input only if the input is in the valid jobs list. This means that the block of code responsible for assigning stats is only reachable when the job input is not valid.

So, if the user enters a valid job on the first attempt, the while loop condition is not met, and the block of code inside the loop, including the stat assignments, is skipped. This could result in stats not being assigned correctly for valid jobs entered on the first try.

By moving the stat assignment block outside the while loop, you ensure that it is executed regardless of whether the user enters a valid job on the first attempt or later attempts. This corrected structure allows for proper assignment of stats based on the user’s job input.:robot:

1 Like

Thank you so much! This has been a big help. This is my first big, real python project, so I am still learning. That probably explains most of this stuff. Solved_places is going to be used at some time. (Have some sort of a plan)

2 Likes

1 Like

Thank you!
My first follower :heart_eyes:

1 Like

Haha you’re welcome! And good luck with the project.
Sorry for the convoluted mess too. I’m kind of a scatter brain.
:joy: