Python Forum
I need help debugging a class.
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
I need help debugging a class.
#1
I am making a game, and I am having an error when I utilize a specific class.
battle = battle("Player", activeplayer.stats["hp"], activeplayer.stats["attack"], activeplayer.stats["defence"], activeplayer.stats["speed"], activeplayer.stats["xp"], activeplayer.stats["gold"], "Dummy", 1, 0, 1, 1, 1, 1, "melee")
           ^
SyntaxError: invalid syntax

To reproduce the error:
Input "n" at the first prompt
Copy the save id
Restart Program
Input "y" at the first prompt
Input the save id at the next prompt
Input "tb" at the next prompt

I assume that I have made a fatal syntax error(s) in the "battle" class (Line 71)
Please point out the locations of the syntax error(s). Thank you in advance. The entire code is below:
import random as r
import pprint
import ast


#stat classes
stat_sets = [{'hp':2, 'attack':2, 'defence':2, 'speed':2, 'gold':0, 'xp':1}, {'hp':2, 'attack':2, 'defence':2, 'speed':2, 'gold':0, 'xp':1}, {'hp':3, 'attack':1, 'defence':2, 'speed':2, 'gold':0, 'xp':1}, {'hp':3, 'attack':2, 'defence':1, 'speed':2, 'gold':0, 'xp':1}, {'hp':3, 'attack':2, 'defence':2, 'speed':1, 'gold':0, 'xp':1}, {'hp':1, 'attack':3, 'defence':2, 'speed':2, 'gold':0, 'xp':1}, {'hp':2, 'attack':3, 'defence':1, 'speed':2, 'gold':0, 'xp':1}, {'hp':2, 'attack':3, 'defence':2, 'speed':1, 'gold':0, 'xp':1}, {'hp':1, 'attack':2, 'defence':3, 'speed':2, 'gold':0, 'xp':1}, {'hp':2, 'attack':1, 'defence':3, 'speed':2, 'gold':0, 'xp':1}, {'hp':2, 'attack':2, 'defence':3, 'speed':1, 'gold':0, 'xp':1}, {'hp':1, 'attack':2, 'defence':2, 'speed':3, 'gold':0, 'xp':1}, {'hp':2, 'attack':1, 'defence':2, 'speed':3, 'gold':0, 'xp':1}, {'hp':2, 'attack':2, 'defence':1, 'speed':3, 'gold':0, 'xp':1}]

hundred = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100]

#RPG Commands BEGIN
commands = {'help':'help_message()','?':'help_message()','Help':'help_message()','testbattle':'eval(test_battle)', 'tb':'eval(test_battle)'}

command_descriptions = {'help':'Displays this message; May be shortened to "?"','testbattle':'Battle with a weak enemy(for testing); May be Shortened to "tb"'}

def help_message():
  for i in command_descriptions:
    print(i + '  -  ' + command_descriptions[i])

test_battle = 'battle = battle("Player", activeplayer.stats["hp"], activeplayer.stats["attack"], activeplayer.stats["defence"], activeplayer.stats["speed"], activeplayer.stats["xp"], activeplayer.stats["gold"], "Dummy", 1, 0, 1, 1, 1, 1, "melee")'
#RPG Commands END

#read database
try:
    f = open('players.txt','r')
    saved_stats = ast.literal_eval(f.read())
    f.close
except:
    saved_stats = {}

#Classes Begin

#item class
class item(object):
    def __init__(self, itemid):
        self.itemid = itemid
    
    pass

#player class
class player(object):
    def __init__(self, stats):
        self.stats = stats
        self.base_hp = self.stats['hp']
        self.base_attack = self.stats['attack']
        self.base_defence = self.stats['defence']
        self.base_speed = self.stats['speed']
        self.gold = self.stats['gold']
        self.xp = self.stats['xp']
    
    def update_stats(self):
        self.hp = self.base_hp + self.xp * self.base_hp * 0.2
        self.atk = self.base_attack + self.xp * self.base_attack * 0.2
        self.defn = self.base_defence + self.xp * self.base_defence * 0.2
        self.spd = self.base_speed + self.xp * self.base_speed * 0.2

    def choose(self):
      choice = input('> ')
      for x in commands:
        if x == choice:
          eval(commands[x])

#class for computer enemies
class enemy(object):
    pass

#class for passive partially intelligent life forms
class passive(object):
    pass

class battle(object):
  def __init__(self, player_name, player_hp, player_atk, player_def, player_spd, player_xp, player_gold, enemy_name, enemy_hp, enemy_atk, enemy_def, enemy_spd, enemy_xp, enemy_gold, enemy_moveset):
    self.over = False
    self.player_name = player_name
    self.player_hp = player_hp
    self.player_atk = player_atk
    self.player_def = player_def
    self.player_spd = player_spd
    self.player_xp = player_xp
    self.player_gold = player_gold
    self.enemy_name = enemy_name
    self.enemy_hp = enemy_hp
    self.enemy_atk = enemy_atk
    self.enemy_def = enemy_def
    self.enemy_spd = enemy_spd
    self.enemy_xp = enemy_xp
    self.enemy_gold = enemy_gold
    self.enemy_moveset

  def calculate(self):
    #who is first
    if self.player_spd > self.enemy.spd:
      self.player_isfirst = True

    elif self.player_spd < self.enemy.spd:
      self.player_isfirst = False

    else:
      self.player_isfirst = bool(r.getrandbits(1))

    #damage ratios
    self.player_ratio = self.enemy_atk - self.player_def
    self.enemy_ratio = self.player_atk - self.enemy_def


  def choose_move(self, enemy_moveset):
    if self.enemy_moveset == 'melee':
      self.enemy_move = {'damage':1.5,'accuracy':99}

    if self.enemy_moveset == 'ranged':
      self.enemy_move = {'damage':2,'accuracy': 75}

    if self.enemy_moveset == 'both':
      self.enemy_move = r.choice([{'damage':1.5,'accuracy': 100}, {'damage':2,'accuracy': 75}])

    self.player_choice = input('> Choose attack(Melee/Ranged):\n Melee - Less Powerful, certain to hit\n Ranged - More Powerful, chance to hit is lower').lower

    if self.player_choice == 'melee':
      self.player_move = {'damage':1.5,'accuracy': 99}

    elif self.player_choice == 'ranged':
      self.player_move = {'damage':2,'accuracy': 75}
    
    else:
      self.player_choice = input('Invalid Input. Try again.\n> Choose attack(Melee/Ranged):\n Melee - Less Powerful, certain to hit\n Ranged - More Powerful, chance to hit is lower').lower

      if self.player_choice == 'melee':
        self.player_move = {'damage':1.5,'accuracy':99}

      elif self.player_choice == 'ranged':
        self.player_move = {'damage':2,'accuracy':75}

      else:
        print('Invalid Input. You will deal no damage.')
        self.player_move = {'damage':0, 'accuracy':0}


  def action(self):
  
    if self.player_isfirst:
      if not r.choice(hundred) > self.player_move['accuracy']:
        self.enemy_hp = self.enemy_hp - self.player_move['damage'] * self.enemy_ratio
        print(self.player_name + 'dealt ' + self.player_move['damage'] * self.enemy_ratio + ' damage to ' + self.enemy_name + '!')

        if self.enemy_hp < 1:
          self.player_win = True
          self.over = True
          self.end()

      else:
        print(self.player_name + ' missed!')
    
    elif not self.over == True:
      if not r.choice(hundred) > self.enemy_move['accuracy']:
        self.player_hp = self.player_hp - self.enemy_move['damage'] * self.player_ratio
        print(self.enemy_name + 'dealt ' + self.enemy_move['damage'] * self.player_ratio + ' damage to ' + self.player_name + '!')

        if self.player_hp < 1:
          self.player_win = False
          self.over = True
          self.end()

      else:
        print(self.enemy_name + ' missed!')

      if not r.choice(hundred) > self.player_move['accuracy']:
        self.enemy_hp = self.enemy_hp - self.player_move['damage'] * self.enemy_ratio
        print(self.player_name + 'dealt ' + self.player_move['damage'] * self.enemy_ratio + ' damage to ' + self.enemy_name + '!')

        if self.enemy_hp < 1:
          self.player_win = True
          self.over = True
          self.end()

      else:
        print(self.player_name + ' missed!')


  def end(self):
    self.over = True
    if not self.player_win:
      print(self.player_name + ' lost!')
      print(self.player_name + ' lost ' + self.player_gold * 1/10 + ' gold as well.')
      self.player_gold = self.player_gold * 9/10
    else:
      print(self.player_name + ' gained ' + self.enemy_gold + ' gold!')
      self.player_gold = self.player_gold + self.enemy_gold
    active_player.xp = self.player_xp
    active_player.gold = self.player_gold

  def start(self):
    self.calculate()
    while not self.over == True:
      active_player.update_stats()
      self.choose_move()
      self.action()


#Classes End

#Functions Begin

#create new player
def new():
    #helpful message 
    print("")
    print("Creating Player...")
    #gen stats
    newplayer = player(stat_sets[r.randint(0,13)])
    #display stats
    print("Player created. Your Stats are:")
    print('HP : {}'.format(str(newplayer.stats['hp'])))
    print('Attack : {}'.format(str(newplayer.stats['attack'])))
    print('Defence : {}'.format(str(newplayer.stats['defence'])))
    print('Speed : {}'.format(str(newplayer.stats['speed'])))
    #gen id of save, user will input this to resume progress
    save_id = r.randint(0,1000000000000)
    #add to temp database
    saved_stats[save_id] = newplayer.stats
    #save to permanet database
    f = open('players.txt','w')
    f.write(str(saved_stats))
    f.close
    #show id of save
    print('Your save id is {}'.format(save_id))
    print('You will be unable to recover your progress if you do not remember your save id.')


#Functions End

#Main Begin

active_player = player({'hp':0, 'attack':0, 'defence':0, 'speed':0, 'gold':0, 'xp':0})
#welcome message
print("Welcome to NightWatch!")
#ask the user to create new save or resume
choice1 = input("Have you played before(Y/N)?\n> ")
#player is resuming save
if choice1 == 'y' or choice1 == 'Y':
    choice2 = int(input("What is your save id(#)?\n> "))
    #search in save ids (This is broken)
    for x in saved_stats:
        if x == choice2:
            #init player class with saved stats
            active_player.stats = saved_stats[x]           
            #show stats
            print('Your saved stats are:')
            print('HP : {}'.format(str(active_player.stats['hp'])))
            print('Attack : {}'.format(str(active_player.stats['attack'])))
            print('Defence : {}'.format(str(active_player.stats['defence'])))
            print('Speed : {}'.format(str(active_player.stats['speed'])))
            print('xp : {}'.format(str(active_player.stats['xp'])))
            print('Gold : {}'.format(str(active_player.stats['gold'])))
else:
    if choice1 == 'n' or choice1 == 'N':
        #user is creating new player/save
        new()

while not active_player.stats['hp'] == 0:
  active_player.choose()
#Main End

        
        
Reply
#2
First of all, it's a pain to go through 260 lines of code finding an error. You should trim down your code to the minimum required to reproduce the error. Often while doing that you will figure out the solution yourself.

Second, you can't eval an assignment, that's why you're getting a syntax error. You need exec for that.

Third, you are using eval way too much. I'm not paranoid about eval the way some pythonistas are, but it should be used only when necessary. You could have a dictionary of the functions, and just call them:

def spam():
    print('Here is your spam.')

def eggs():
    print('Here is your spam and eggs.')

commands = {'spam': spam, 'eggs': eggs}

choice = input('Would you like spam or eggs? ')
commands[choice]()    # Note the parentheses, which call the function returned from the dictionary
Craig "Ichabod" O'Brien - xenomind.com
I wish you happiness.
Recommended Tutorials: BBCode, functions, classes, text adventures
Reply
#3
I haven't run it but there are some items you can optimize. The issue appears to be with eval() actually. I tried a simple string to eval and got the same error:

x = "x = 5"
eval(x)
Error:
Traceback (most recent call last): File "<pyshell#1>", line 1, in <module> eval(x) File "<string>", line 1 x = 5 ^ SyntaxError: invalid syntax
eval() cannot create the variable that's being instantiated in the string. Instead, it would have to be:

x = "5"
y = eval(x)
As for a few other items to help you clean up:

  1. hundred is unncessary. Instead of using random.choice(hundred), you can use random.randit(1,100) and get the same effect without using memory to store the list.
  2. player() has a lot of redundancy. It should have either the stats dict or the stats as individual attributes, but not both. Having both could lead to errors later if the methods for adjusting them are inconsistent - some altering stats and others altering the attributes. For instance, line 239 updates the stats but it doesn't change anything else. That could cause bugs later on.
  3. battle() should not have that many parameters for instantiation. Instead, pass in a player() and an enemy() and then invoke the methods of those objects within battle() to access their data.
    class battle(object):
      def __init__(self, player, enemy):
        self.over = False
        self.player = player
        self.enemy = enemy
  4. For your user inputs, I recommend calling str.lower() to compensate for users entering "Y" instead of "y". This makes the logic cleaner. Also, give a list to accept even more variety.
    choice1 = input("Have you played before(Y/N)?\n> ").lower()
    if choice1 in ('y', 'yes', 'yeah', 'yay', 'yep', 'yup'):
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020