Search code examples
pythonpygameself

main() missing 1 required positional argument: 'self'


Here is my code. I am following a tutorial for making a snake game in python.

import math, random, pygame

class cube(object):
    rows = 20
    w = 500
    def __init__(self,start,dirnx=1,dirny=0,color=(255,0,0)):
        self.pos = start
        self.dirnx = 1
        self.dirny = 0
        self.color = color

    def move(self, dirnx, dirny):
        self.dirnx = dirnx
        self.dirny = dirny
        self.pos = (self.pos[0] + self.dirnx, self.pos[1] + self.dirny)
        
    def draw(self, surface, eyes=False):
        dis = self.w // self.rows  
        i = self.pos[0]
        j = self.pos[1] 
        pygame.draw.rect(surface, self.color, (i*dis+1,j*dis+1, dis-2, dis-2))
        if eyes: 
            centre = dis//2
            radius = 3
            circleMiddle = (i*dis+centre-radius,j*dis+8) 
            circleMiddle2 = (i*dis + dis -radius*2, j*dis+8) 
            pygame.draw.circle(surface, (0,0,0), circleMiddle, radius)
            pygame.draw.circle(surface, (0,0,0), circleMiddle2, radius) 


class snake(object):
    body = []
    turns = {}
    def __init__(self, color, pos):
        self.color = color
        self.head = cube(pos)  
        self.body.append(self.head) 

        self.dirnx = 0 
        self.dirny = 1 

    def move():
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()

            keys = pygame.key.get_pressed()
            
            for key in keys: 
                if keys[pygame.K_LEFT]:
                    self.dirnx = -1
                    self.dirny = 0 
                    self.turns[self.head.pos[:]] = [self.dirnx, self.dirny] 
                    
                elif keys[pygame.K_RIGHT]:
                    self.dirnx = 1
                    self.dirny = 0
                    self.turns[self.head.pos[:]] = [self.dirnx, self.dirny]

                elif keys[pygame.K_UP]:
                    self.dirnx = 0
                    self.dirny = -1
                    self.turns[self.head.pos[:]] = [self.dirnx, self.dirny]

                elif keys[pygame.K_DOWN]:
                    self.dirnx = 0
                    self.dirny = 1
                    self.turns[self.head.pos[:]] = [self.dirnx, self.dirny]
            
        for i, c in enumerate(self.body): 
            p = c.pos[:] 
            if p in self.turns: 
                turn = self.turns 
                c.move(turn[0], turn[1]) 
                if i == len(self.body)-1: 
                    self.turns.pop(p)
            else: 
                if c.dirnx == -1 and c.pos[0] <= 0: c.pos = (c.rows-1, c.pos[1]) 
                elif c.dirnx == 1 and c.pos[0] >= c.rows-1: c.pos = (0,c.pos[1]) 
                elif c.dirny == 1 and c.pos[1] >= c.rows-1: c.pos = (c.pos[0], 0) 
                elif c.dirny == -1 and c.pos[1] <= 0: c.pos = (c.pos[0],c.rows-1)
                else: c.move(c.dirnx, c.dirny)

    def reset(self, pos):
        pass

    def addCube(self):
        tail = self.body[-1] #-1 is the last element in that list
        dx, dy = tail.dirnx, tail.dirny #dx and dy is direction of x and direction of y

        if dx == 1 and dy == 0:
            self.body.append(cube((tail.pos[0]-1,tail.pos[1])))  
        elif dx == -1 and dy == 0:
            self.body.append(cube((tail.pos[0]+1,tail.pos[1])))
        elif dx == 0 and dy == 1:
            self.body.append(cube((tail.pos[0],tail.pos[1]-1)))
        elif dx == 0 and dy == -1:
            self.body.append(cube((tail.pos[0],tail.pos[1]+1)))

            self.body[-1].dirnx = dx 
            self.body[-1].dirny = dy
    def draw(self, surface):
        for i, c in enumerate(self.body):
            if i==0: 
                c.draw(surface, True) 
            else:
                c.draw(surface) 

    def drawGrid(w, rows, surface):
        sizeBtwn = w // rows  

        x = 0  
        y = 0  
        for l in range(rows):
            x = x + sizeBtwn
            y = y + sizeBtwn 

            pygame.draw.line(surface, (255,255,255), (x,0),(x,w))
            pygame.draw.line(surface, (255,255,255), (0,y),(w,y))

    def redrawWindow(surface):
        global width, rows, s, snack #ADD snack to this line  
        surface.fill((0,0,0))
        s.draw(surface)
        snack.draw(surface) #NEW
        drawGrid(width, rows, surface) 
        pygame.display.update() 

    def randomSnack(rows, item):
        positions = item.body
        while True:
            x = random.randrange(rows) 
            y = random.randrange(rows)
            if len(list(filter(lambda z:z.pos == (x,y), positions))) > 0:
                continue
            else:
                break
            return (x.y)

    def message_box(subject, content):
        pass

    def main(self):
        global width, rows, s, snack
        self.move = move
        width = 500 
        height = 500 
        rows = 20 
        win = pygame.display.set_mode((width, width)) 
        s = snake ((255,0,0), (10,10))
        snack = cube(randomSnack(rows, s), color = (0,255,0))
        flag = True
        
        clock = pygame.time.Clock() 
        
        while flag:
            pygame.time.delay(50)
            clock.tick(10)
            s.move()
            if s.body[0] == snake.pos: #Checks if the head collides with the snack
                s.addCube() #Adds a new cube to the snake
                snack = cube(randomSnack(rows, s), color=(0,255,0)) #Creates a new snack object

            redrawWindow(win) 

main()

But then I keep getting this error which I can't seem to fix:

Traceback (most recent call last):
File "C:\Users\Ammar Khan\AppData\Local\Programs\Python\Python37\Snake Game Tutorial #4.py", line 186, in <module> main()
TypeError: main() missing 1 required positional argument: 'self'

Solution

  • In the code, the indenting shows that your main () function is in your snake class, and so are all the methods from drawGrid() down. You need shift them all the back by one indent to take them out of the class. Then get rid of the self argument in your def main(). That should fix that issue.

    Another thing I noticed is that your move() method that is within the snake() class is missing the self argument that it should have. It should be move(self). All instance methods in a class MUST have a self argument as their first argument (actually self is just the agreed upon convention for the name, it could be anything but you should use self or it will be confusing). You do not call it by passing that argument, it gets filled in by python with a reference to the instance.

    A lot of beginner python programmers get thrown by this when they first start making their own classes. It may seem confusing, since there that argument is not there when you call it, and so it may seem like you are calling it with one argument too few.

    I will illustrate how it works. If you had a class player and wanted to call its update() method, you would call player.update(...) with whatever args it needed. Python when calling that instance method, takes the instance reference player and puts it in front of all the '...' arguments that you passed yourself and it ends up in the self parameter. That is how the instance method gets its reference to its own instance that it can use to access its internal attributes. I hope that helps clarify this.

    I noticed two other things:

    1) You have two definitions of class cube() in your code. The second one overwrites the first causing it to be ignored though. They start off the same, so perhaps that is a typo or copy and past error?

    2) You seem to have a typo here:

    if s.body[0] == snake.pos: #Checks if the head collides with the snack
    

    From the comment, I believe that it should be snack.pos, not snake.