Problem ist in der Zeile: pygame.draw.rect(win, (255, 0, 0), [foodPos(0), foodPos(1), 10, 10])
Fehlermeldung: 'NoneType' object is not callable
<import pygame
import random
class Snake():
def __init__(self):
self.position = [100,50]
self.body = [[100,50],[90,50],[80,50]]
self.direction = "RIGHT"
self.changeDirectionTo = self.direction
def changeDirTo(self, dir):
if dir=="RIGHT" and not self.direction=="LEFT":
self.direction=="RIGHT"
if dir=="LEFT" and not self.direction=="RIGHT":
self.direction=="LEFT"
if dir=="UP" and not self.direction=="DOWN":
self.direction=="UP"
if dir=="DOWN" and not self.direction=="UP":
self.direction=="DOWN"
def move(self, foodPos):
if self.direction == "RIGHT":
self.position[0] += 10
if self.direction == "LEFT":
self.position[0] -= 10
if self.direction == "UP":
self.position[1] += 10
if self.direction == "RIGHT":
self.position[1] -= 10
self.body.insert(0, list(self.position))
if self.position == foodPos:
return 1
else:
self.body.pop()
return 0
def checkCollision(self):
if self.position[0] > 490 or self.postion[0] < 0:
return 1
elif self.position[1] > 490 or self.position[1] < 0:
return 1
for bodyPart in self.body[1:]:
if self.position == bodyPart:
return 1
return 0
def getHeadPos(self):
return self.position
def getBody(self):
return self.body
class FoodSpawner():
def __init__(self):
self.position = [random.randrange(1, 50) * 10, random.randrange(1, 50) * 10]
self.isFoodOnScreen = True
def spawnFood(self):
if self.isFoodOnScreen == False:
self.position = [random.randrange(1, 50) * 10, random.randrange(1, 50) * 10]
self.isFoodOnScreen = True
return self.position
def setFoodOnScreen(self, b):
self.isFoodOnScreen = b
def gameover():
pygame.quit()
win = pygame.display.set_mode((500, 500))
pygame.display.set_caption("Snake Game")
fps = pygame.time.Clock()
score = 0
snake = Snake()
foodSpawner = FoodSpawner()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameover()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
snake.changeDirectionTo('RIGHT')
if event.key == pygame.K_LEFT:
snake.changeDirectionTo('LEFT')
if event.key == pygame.K_UP:
snake.changeDirectionTo('Up')
if event.key == pygame.K_DOWN:
snake.changeDirectionTo('DOWN')
foodPos = foodSpawner.spawnFood()
if snake.move(foodPos) == 1:
score += 1
foodSpawner.setFoodOnScreen(False)
win.fill((255,255,255))
for pos in snake.getBody():
pygame.draw.rect(win, (0, 255, 0), (pos[0], pos[1], 10, 10))
pygame.draw.rect(win, (255, 0, 0), [foodPos(0), foodPos(1), 10, 10])
if snake.checkCollision() == 1:
gameover()
pygame.display.set_caption("Snake Game | Score: " + str(score))
pygame.display.flip()
fps.tick(24)>
Wie löse ich dieses Problem?
-
- User
- Beiträge: 1
- Registriert: Samstag 21. März 2020, 19:43
Zuletzt geändert von Leo Python am Samstag 21. März 2020, 20:05, insgesamt 1-mal geändert.
Diese Zeilen werfen bei mir Fehler:
Des Weiteren solltest du dir die Funktion def checkCollision(self):
genauer anschauen, die gibt beim ersten Aufruf sofort True aus !
Code: Alles auswählen
pygame.draw.rect(win, (255, 0, 0), [foodPos(0), foodPos(1), 10, 10])
Code: Alles auswählen
if self.position[0] > 490 or self.postion[0] < 0:
genauer anschauen, die gibt beim ersten Aufruf sofort True aus !
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
- __blackjack__
- User
- Beiträge: 14052
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@Leo Python: Lass Dir vor der Zeile doch mal alle beteiligten Werte ausgeben und sag dann was Dir an der Fehlermeldung nicht klar ist, und was Du da als Ergebnis bei den konkreten Werten erwartet hättest.
Empfehlenswert finde ich die externe Logging-Bibliothek `loguru` weil die unter anderem einen Dekorator für Funktione/Methoden beziehungsweise Kontextmanager bietet, mit dem automatisch eine Ausnahme in der Funktion/Methode/Block protokolliert wird, und zwar mit etwas mehr Informationen als der normale Traceback von Python. Wenn ich den Code auf Modulebene in ein ``with logger.catch():`` verpacke, wird das hier ausgegeben:
Hier sollte leicht ersichtlich sein an welcher Stelle in diesem Ausdruck die Ausnahmen ausgelöst wird.
Empfehlenswert finde ich die externe Logging-Bibliothek `loguru` weil die unter anderem einen Dekorator für Funktione/Methoden beziehungsweise Kontextmanager bietet, mit dem automatisch eine Ausnahme in der Funktion/Methode/Block protokolliert wird, und zwar mit etwas mehr Informationen als der normale Traceback von Python. Wenn ich den Code auf Modulebene in ein ``with logger.catch():`` verpacke, wird das hier ausgegeben:
Code: Alles auswählen
2020-03-22 11:44:21.079 | ERROR | __main__:<module>:119 - An error has been caught in function '<module>', process 'MainProcess' (14791), thread 'MainThread' (140040442341184):
Traceback (most recent call last):
> File "./forum16.py", line 114, in <module>
pygame.draw.rect(win, (255, 0, 0), [foodPos(0), foodPos(1), 10, 10])
│ │ │ │ │ └ None
│ │ │ │ └ None
│ │ │ └ <Surface(500x500x32 SW)>
│ │ └ <built-in function rect>
│ └ <module 'pygame.draw' from '/usr/local/lib/python3.6/dist-packages/pygame/draw.cpython-36m-x86_64-linux-gnu.so'>
└ <module 'pygame' from '/usr/local/lib/python3.6/dist-packages/pygame/__init__.py'>
TypeError: 'NoneType' object is not callable
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Coole Bibliothek.
Ich vermute mal es handelt sich um eine Schulaufgabe, die zum Zweck der Verschleierung aus einem öffentlich verfügbaren Beispiel unter Verwendung eines Hackmessers zurechtgewerkelt wurde.
https://github.com/simallaire/snake-pyt ... spawner.py
Ich vermute mal es handelt sich um eine Schulaufgabe, die zum Zweck der Verschleierung aus einem öffentlich verfügbaren Beispiel unter Verwendung eines Hackmessers zurechtgewerkelt wurde.
https://github.com/simallaire/snake-pyt ... spawner.py
- __blackjack__
- User
- Beiträge: 14052
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@Leo Python: Sonstige Anmerkungen zum Quelltext:
Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.
Ich vermisse da irgendie ein `pygame.init()`. Interessant dass das auch ohne laufen kann, aber garantiert ist das nicht.
FPS ist üblicherweise die Abkürzung für „frames per second“ und damit kein Wirklich guter Name für ein `Clock`-Objekt.
Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase). Siehe auch den Style Guide for Python Code.
`gameover()` ist kein guter Name für eine Funktion. Funktions- und Methodennamen beschreiben in der Regel eine Tätigkeit, damit der Leser weiss was die Funktion/Methode tut und um sie leichter von eher passiven Werten unterscheiden zu können.
Statt überall die Zeichenketten "LEFT", "RIGHT", usw. zu schreiben würde man sich besser Konstanten definieren, dann kann es nicht wie im Hauptprogramm passieren das man sich an einer Stelle dann mal verschreibt und das nicht zu einer Ausnahme führt als wenn man sich bei einem Namen oder Attributnamen vertippt. Solche Fehler übersieht man dann leicht oder man sucht danach länger als es nötig wäre. Das `enum`-Modul bietet sich hier an um nicht nur die Konstanten zu definieren, sondern sie auch gleich thematisch zu einem Typ zusammenzufassen.
Das `changeDirectionTo`-Attribut von `Snake` macht mit dem Namen keinen sinn. Das klingt wie eine Methode oder Funktion, weil der Name eine Tätigkeit beschreibt, aber das ist einfach nur ein passiver Wert, nämlich die aktuelle (Aus)Richtung des Kopfes. Du machst dann an anderer Stelle auch prompt den Fehler und versuchst das aufzurufen, was nicht geht. Aufgerufen werden sollte die `changeDirTo()`-Methode, wobei die eigentlich `changeDirectionTo()` heissen sollte, denn Abkürzungen in Namen sollte man vermeiden. (Also eigentlich `change_direction_to()` nach PEP8.)
Die Ausrichtung des Kopfes gibt es ja bereits als `direction`-Attribut, damit ist `changeDirectionTo` auch noch redundant und damit überflüssig. Wird im restlichen Code auch ausser dem falschen Versuch das aufzurufen gar nicht verwendet.
Ebenfalls redundant ist das `position`-Attribut, weil der gleiche Wert immer an erster Stelle im `body` steckt. Das würde ich als `property()` modellieren.
Im `FoodSpawner` wiederholt sich Code der da nur einmal stehen sollte. Letztlich finde ich aber die gesamte Klasse sehr merkwürdig. `spawn_food()` sollte immer eine neue Position liefern bei dem Methodennamen. Mir fällt aber auch kein sinnvoller Name für das tatsächliche Verhalten der Methode ein, und warum man *das* bisschen jetzt unbedingt in eine Klasse mit einer Methode neben der `__init__()` und einem triviallen Setter den man so in Python nicht schreiben würde, stecken muss. Als Nahrungsposition sollte man einfach ein Tupel verwenden und den Wert `None` wenn keine Position/Nahrung mehr auf dem Spielfeld ist.
Insgesamt sollte man für die Positionen übrigens Tupel statt Listen verwenden. Listen sind für Sequenzen bei denen jedes Element die gleiche Bedeutung hat. Wenn die Bedeutung des Elements von der Positionen abhängig ist, dann verwendet man Tupel.
Man sollte magische Zahlen im Code vermeiden und dafür Konstanten definieren. Wenn Du die Grösse der Felder mal ändern möchtest, musst Du durch den gesamten Quelltext gehen und bei jeder 10 die dort irgendwo steht, entscheiden ob das die Feldgrösse ist und angepasst werden muss, oder ob die an der Stelle eine andere Bedeutung hat und nicht angefasst werden darf. Wenn man am Anfang einmal eine Konstante ``GRID_SIZE = 10`` definiert und alles was von der Feldgrösse abhängt draus berechnet, dann kann man die Feldgrösse ganz einfach an dieser einen Stelle anpassen.
Auch die Spielfeld-Grösse (in Feldern und nicht in Pixeln) sollte man als Konstante(n) definieren, und man muss natürlich auch alle Zahlwerte die sich auf eine oder beide dieser Konstanten beziehen, im Code daraus ausrechnen.
Ebenfalls für Konstanten würden sich die Tupel anbieten die Farben beschreiben.
Triviale Getter/Setter schreibt man in Python nicht. Statt `getBody()` greift man einfach auf das Attribut `body` zu.
In `changeDirTo()` machen die Zweige alle *nichts*. Da wird nur ein Vergleich ausgeführt und dessen Ergebnis ignoriert. Du wolltest da wohl eine Zuweisung schreiben.
Das könnte mit mit Hilfe einer Funktion die zu einer Richtung die Gegenrichtung liefert auch deutlich kürzer und IMHO verständlicher ausdrücken. Wenn man es bei den ``if``\s belässt, sollte man auch ``elif`` nutzen, denn die einzelnen Zweige schliessen sich ja gegenseitig aus. Ausserdem würde ich noch ein ``else`` am Ende anhängen für den Fall das ein ungültiger Wert als Richtung übergeben wurde.
Python hat einen Datentyp für Wahrheitswerte (`bool`) mit den Werten `True` und `False`. Da sollte man nicht die Zahlen 1 und 0 für missbrauchen.
`checkCollision()` lässt sich deutlich vereinfachen. Als erstes mal kann man die Kopfposition lokal an die Namen `x` und `y` binden, dann hat man da nicht ``self.position[0]`` und ``self.position[1]`` in den Bedingungen zu stehen.
Dann kann man statt logischer Verknüpfung mit ``or`` und wiederholung des Namens der Verglichen wird, mit Operatorverkettung und ``not`` die beiden Grenztests für `x` und y` leichter lesbar gestalten. Aus ``if x >= FIELD_SIZE * GRID_SIZE or x < 0:`` wird dann ``if not 0 <= x < FIELD_SIZE * GRID_SIZE:``. Für `y` analog.
Die Schleife danach lässt sich mit dem ``in``-Operator ganz einfach als ``if self.position in self.body[1:]:`` ausdrücken. Und dann hat man drei ``if``-Bedingungen die in einem Ausdruck zusammengeführt werden können der selbst bereits den Rückgabewert liefert.
Zwischenstand:
Ich würde da noch die Spielkoordinaten von den Bildschirmkoordinaten trennen, also das die Schlangeteile und Nahrung Positionen von (0, 0) bis (49, 49) annehmen können und die Bildschirmkoordinaten erst beim zeichenen daraus berechnet werden.
Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.
Ich vermisse da irgendie ein `pygame.init()`. Interessant dass das auch ohne laufen kann, aber garantiert ist das nicht.
FPS ist üblicherweise die Abkürzung für „frames per second“ und damit kein Wirklich guter Name für ein `Clock`-Objekt.
Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase). Siehe auch den Style Guide for Python Code.
`gameover()` ist kein guter Name für eine Funktion. Funktions- und Methodennamen beschreiben in der Regel eine Tätigkeit, damit der Leser weiss was die Funktion/Methode tut und um sie leichter von eher passiven Werten unterscheiden zu können.
Statt überall die Zeichenketten "LEFT", "RIGHT", usw. zu schreiben würde man sich besser Konstanten definieren, dann kann es nicht wie im Hauptprogramm passieren das man sich an einer Stelle dann mal verschreibt und das nicht zu einer Ausnahme führt als wenn man sich bei einem Namen oder Attributnamen vertippt. Solche Fehler übersieht man dann leicht oder man sucht danach länger als es nötig wäre. Das `enum`-Modul bietet sich hier an um nicht nur die Konstanten zu definieren, sondern sie auch gleich thematisch zu einem Typ zusammenzufassen.
Das `changeDirectionTo`-Attribut von `Snake` macht mit dem Namen keinen sinn. Das klingt wie eine Methode oder Funktion, weil der Name eine Tätigkeit beschreibt, aber das ist einfach nur ein passiver Wert, nämlich die aktuelle (Aus)Richtung des Kopfes. Du machst dann an anderer Stelle auch prompt den Fehler und versuchst das aufzurufen, was nicht geht. Aufgerufen werden sollte die `changeDirTo()`-Methode, wobei die eigentlich `changeDirectionTo()` heissen sollte, denn Abkürzungen in Namen sollte man vermeiden. (Also eigentlich `change_direction_to()` nach PEP8.)
Die Ausrichtung des Kopfes gibt es ja bereits als `direction`-Attribut, damit ist `changeDirectionTo` auch noch redundant und damit überflüssig. Wird im restlichen Code auch ausser dem falschen Versuch das aufzurufen gar nicht verwendet.
Ebenfalls redundant ist das `position`-Attribut, weil der gleiche Wert immer an erster Stelle im `body` steckt. Das würde ich als `property()` modellieren.
Im `FoodSpawner` wiederholt sich Code der da nur einmal stehen sollte. Letztlich finde ich aber die gesamte Klasse sehr merkwürdig. `spawn_food()` sollte immer eine neue Position liefern bei dem Methodennamen. Mir fällt aber auch kein sinnvoller Name für das tatsächliche Verhalten der Methode ein, und warum man *das* bisschen jetzt unbedingt in eine Klasse mit einer Methode neben der `__init__()` und einem triviallen Setter den man so in Python nicht schreiben würde, stecken muss. Als Nahrungsposition sollte man einfach ein Tupel verwenden und den Wert `None` wenn keine Position/Nahrung mehr auf dem Spielfeld ist.
Insgesamt sollte man für die Positionen übrigens Tupel statt Listen verwenden. Listen sind für Sequenzen bei denen jedes Element die gleiche Bedeutung hat. Wenn die Bedeutung des Elements von der Positionen abhängig ist, dann verwendet man Tupel.
Man sollte magische Zahlen im Code vermeiden und dafür Konstanten definieren. Wenn Du die Grösse der Felder mal ändern möchtest, musst Du durch den gesamten Quelltext gehen und bei jeder 10 die dort irgendwo steht, entscheiden ob das die Feldgrösse ist und angepasst werden muss, oder ob die an der Stelle eine andere Bedeutung hat und nicht angefasst werden darf. Wenn man am Anfang einmal eine Konstante ``GRID_SIZE = 10`` definiert und alles was von der Feldgrösse abhängt draus berechnet, dann kann man die Feldgrösse ganz einfach an dieser einen Stelle anpassen.
Auch die Spielfeld-Grösse (in Feldern und nicht in Pixeln) sollte man als Konstante(n) definieren, und man muss natürlich auch alle Zahlwerte die sich auf eine oder beide dieser Konstanten beziehen, im Code daraus ausrechnen.
Ebenfalls für Konstanten würden sich die Tupel anbieten die Farben beschreiben.
Triviale Getter/Setter schreibt man in Python nicht. Statt `getBody()` greift man einfach auf das Attribut `body` zu.
In `changeDirTo()` machen die Zweige alle *nichts*. Da wird nur ein Vergleich ausgeführt und dessen Ergebnis ignoriert. Du wolltest da wohl eine Zuweisung schreiben.
Das könnte mit mit Hilfe einer Funktion die zu einer Richtung die Gegenrichtung liefert auch deutlich kürzer und IMHO verständlicher ausdrücken. Wenn man es bei den ``if``\s belässt, sollte man auch ``elif`` nutzen, denn die einzelnen Zweige schliessen sich ja gegenseitig aus. Ausserdem würde ich noch ein ``else`` am Ende anhängen für den Fall das ein ungültiger Wert als Richtung übergeben wurde.
Python hat einen Datentyp für Wahrheitswerte (`bool`) mit den Werten `True` und `False`. Da sollte man nicht die Zahlen 1 und 0 für missbrauchen.
`checkCollision()` lässt sich deutlich vereinfachen. Als erstes mal kann man die Kopfposition lokal an die Namen `x` und `y` binden, dann hat man da nicht ``self.position[0]`` und ``self.position[1]`` in den Bedingungen zu stehen.
Dann kann man statt logischer Verknüpfung mit ``or`` und wiederholung des Namens der Verglichen wird, mit Operatorverkettung und ``not`` die beiden Grenztests für `x` und y` leichter lesbar gestalten. Aus ``if x >= FIELD_SIZE * GRID_SIZE or x < 0:`` wird dann ``if not 0 <= x < FIELD_SIZE * GRID_SIZE:``. Für `y` analog.
Die Schleife danach lässt sich mit dem ``in``-Operator ganz einfach als ``if self.position in self.body[1:]:`` ausdrücken. Und dann hat man drei ``if``-Bedingungen die in einem Ausdruck zusammengeführt werden können der selbst bereits den Rückgabewert liefert.
Zwischenstand:
Code: Alles auswählen
#!/usr/bin/env python3
import random
from enum import auto, Enum
import pygame
from loguru import logger
GRID_SIZE = 10
FIELD_SIZE = 50
BACKGROUND_COLOR = (255, 255, 255)
FOOD_COLOR = (255, 0, 0)
SNAKE_COLOR = (0, 255, 0)
class Direction(Enum):
LEFT = auto()
RIGHT = auto()
UP = auto()
DOWN = auto()
@property
def opposite(self):
return _DIRECTION_TO_OPPOSITE[self]
_DIRECTION_TO_OPPOSITE = {
Direction.LEFT: Direction.RIGHT,
Direction.RIGHT: Direction.LEFT,
Direction.UP: Direction.DOWN,
Direction.DOWN: Direction.UP,
}
class Snake:
def __init__(self):
self.body = [
(10 * GRID_SIZE, 5 * GRID_SIZE),
(9 * GRID_SIZE, 5 * GRID_SIZE),
(8 * GRID_SIZE, 5 * GRID_SIZE),
]
self.direction = Direction.RIGHT
@property
def position(self):
return self.body[0]
def change_direction_to(self, direction):
if direction is not self.direction.opposite:
self.direction = direction
def move(self, food_position):
x, y = self.position
if self.direction == Direction.RIGHT:
x += GRID_SIZE
elif self.direction == Direction.LEFT:
x -= GRID_SIZE
elif self.direction == Direction.DOWN:
y += GRID_SIZE
elif self.direction == Direction.UP:
y -= GRID_SIZE
else:
assert False, f"unkown direction {self.direction}"
self.body.insert(0, (x, y))
if self.position == food_position:
return True
else:
self.body.pop()
return False
def check_collision(self):
x, y = self.position
return (
not 0 <= x < FIELD_SIZE * GRID_SIZE
or not 0 <= y < FIELD_SIZE * GRID_SIZE
or self.position in self.body[1:]
)
@logger.catch()
def main():
pygame.init()
try:
window = pygame.display.set_mode(
(FIELD_SIZE * GRID_SIZE, FIELD_SIZE * GRID_SIZE)
)
pygame.display.set_caption("Snake Game")
clock = pygame.time.Clock()
score = 0
snake = Snake()
food_position = None
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
snake.change_direction_to(Direction.RIGHT)
if event.key == pygame.K_LEFT:
snake.change_direction_to(Direction.LEFT)
if event.key == pygame.K_UP:
snake.change_direction_to(Direction.UP)
if event.key == pygame.K_DOWN:
snake.change_direction_to(Direction.DOWN)
if snake.move(food_position):
food_position = None
score += 1
if not food_position:
food_position = (
random.randrange(1, FIELD_SIZE) * GRID_SIZE,
random.randrange(1, FIELD_SIZE) * GRID_SIZE,
)
window.fill(BACKGROUND_COLOR)
for position in snake.body:
pygame.draw.rect(
window,
SNAKE_COLOR,
(position[0], position[1], GRID_SIZE, GRID_SIZE),
)
pygame.draw.rect(
window,
FOOD_COLOR,
(food_position[0], food_position[1], GRID_SIZE, GRID_SIZE),
)
if snake.check_collision():
break
pygame.display.set_caption(f"Snake Game | Score: {score}")
pygame.display.flip()
clock.tick(24)
finally:
pygame.quit()
if __name__ == "__main__":
main()
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Mal wieder war __blackjack__ schneller 
Hier meine Version, auf die Schnelle, einfach ein working prototype.

Hier meine Version, auf die Schnelle, einfach ein working prototype.
Code: Alles auswählen
import pygame as pg
import random
BLACK = (0, 0, 0)
BLUE = (0, 0, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
WHITE = (255, 255, 255)
CANVAS_WIDTH = 500
CANVAS_HEIGHT = 500
BODY_SIZE = 20
MAX_X_POS = CANVAS_WIDTH//BODY_SIZE
MAX_Y_POS = CANVAS_HEIGHT//BODY_SIZE
DIRECTIONS = [pg.K_UP, pg.K_RIGHT, pg.K_DOWN, pg.K_LEFT]
DXDY = {pg.K_UP: (0, -1), pg.K_RIGHT: (1, 0), pg.K_DOWN: (0, 1), pg.K_LEFT: (-1, 0)}
class Snake():
def __init__(self):
self.body = [self.random_position()]
self.direction = random.choice(DIRECTIONS)
self.spawn_food()
@staticmethod
def random_position():
x, y = random.randrange(0, MAX_X_POS), random.randrange(0, MAX_Y_POS)
return (x, y)
def spawn_food(self):
while True:
position = self.random_position()
if position not in self.body:
self.food_pos = position
return
def move(self, canvas):
x, y = self.body[-1]
dx, dy = DXDY[self.direction]
new_head = (x+dx, y+dy)
if not self.hit_myself(new_head):
if not self.hit_wall(new_head):
self.body.append(new_head)
if new_head == self.food_pos:
self.spawn_food()
else:
self.body.pop(0)
self.draw(canvas)
return False
return True
def hit_myself(self, new_head):
return new_head in self.body
def hit_wall(self, new_head):
x, y = new_head
return any((x < 0, y < 0, x > MAX_X_POS, y > MAX_Y_POS))
def draw(self, canvas):
x, y = self.food_pos
pg.draw.rect(canvas, GREEN, (x * BODY_SIZE, y*BODY_SIZE, BODY_SIZE, BODY_SIZE))
for x, y in self.body:
pg.draw.rect(canvas, WHITE, (x * BODY_SIZE, y*BODY_SIZE, BODY_SIZE, BODY_SIZE))
def main():
pg.init()
canvas = pg.display.set_mode((CANVAS_WIDTH, CANVAS_HEIGHT))
pg.display.set_caption("Snake Game")
clock = pg.time.Clock()
snake = Snake()
game_finished = False
while not game_finished:
for event in pg.event.get():
if event.type == pg.QUIT:
game_finished = True
break
elif event.type == pg.KEYDOWN:
if event.key in DIRECTIONS:
snake.direction = event.key
if not game_finished:
canvas.fill(BLACK)
collision = snake.move(canvas)
if collision:
canvas.fill(RED)
game_finished = True
pg.display.update()
clock.tick(5)
pg.quit()
if __name__ == '__main__':
main()
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png