Runs a simple Mario game:
import collections, dataclasses, enum, io, itertools, math, pygame, random, urllib
-from urllib.request import urlopen
-
-D = enum.Enum('D', 'n e s w')
-P = collections.namedtuple('P', 'x y')
-Mario = dataclasses.make_dataclass('Mario', ['rect', 'spd', 'facing_left', 'running_cycle'])
-RECT_SIDE, SCR_SIDE, MAX_SPEED = 16, 25, P(x=5, y=10)
-COORDS = [p for p in itertools.product(range(SCR_SIDE), repeat=2) if {*p} & {0, SCR_SIDE-1}] +\
- [(random.randint(1, SCR_SIDE-2), random.randint(2, SCR_SIDE-2)) for _ in range(62)]
-FLOORS = [pygame.Rect(x*RECT_SIDE, y*RECT_SIDE, RECT_SIDE, RECT_SIDE) for x, y in COORDS]
-URL = 'https://raw.githubusercontent.com/gto76/python-cheatsheet/master/web/mario_bros.png'
-IMAGE = pygame.image.load(io.BytesIO(urlopen(URL).read()))
-FRAMES = [IMAGE.subsurface(pygame.Rect(x*16, 0, 16, 16)) for x in range(7)]
-FRAMES += [pygame.transform.flip(f, True, False) for f in FRAMES]
-TILES = [IMAGE.subsurface(pygame.Rect(x*16, 0, 16, 16)) for x in range(9, 14)]
+#Pygame
Example
Runs a simple Super Mario game:
import collections, dataclasses, enum, io, math, pygame, urllib.request, itertools as it
+from random import randint
+
+P = collections.namedtuple('P', 'x y')
+D = enum.Enum('D', 'n e s w')
+SIZE, MAX_SPEED = 25, P(5, 10)
def main():
- pygame.init()
- screen = pygame.display.set_mode(2 * [SCR_SIDE * RECT_SIDE])
- mario = Mario(pygame.Rect(16, 16, 16, 16), P(0, 0), False, itertools.cycle(range(3)))
- while not any(event.type == pygame.QUIT for event in pygame.event.get()):
+ def get_rect(x, y):
+ return pygame.Rect(x*16, y*16, 16, 16)
+ def get_images():
+ url = 'https://gto76.github.io/python-cheatsheet/web/mario_bros.png'
+ img = pygame.image.load(io.BytesIO(urllib.request.urlopen(url).read()))
+ return [img.subsurface(get_rect(x, 0)) for x in range(img.get_width() // 16)]
+ def get_mario():
+ Mario = dataclasses.make_dataclass('Mario', 'rect spd facing_left frame_cycle'.split())
+ return Mario(get_rect(1, 1), P(0, 0), False, it.cycle(range(3)))
+ def get_tiles():
+ positions = [p for p in it.product(range(SIZE), repeat=2) if {*p} & {0, SIZE-1}] + \
+ [(randint(1, SIZE-2), randint(2, SIZE-2)) for _ in range(SIZE**2 // 10)]
+ return [get_rect(*p) for p in positions]
+ def get_screen():
+ pygame.init()
+ return pygame.display.set_mode(2 * [SIZE*16])
+ run(get_images(), get_mario(), get_tiles(), get_screen())
+
+def run(images, mario, tiles, screen):
+ while all(event.type != pygame.QUIT for event in pygame.event.get()):
keys = {pygame.K_UP: D.n, pygame.K_RIGHT: D.e, pygame.K_DOWN: D.s, pygame.K_LEFT: D.w}
pressed = {keys.get(i, None) for i, on in enumerate(pygame.key.get_pressed()) if on}
- update_speed(mario, pressed)
- update_position(mario)
- draw(screen, mario, pressed)
+ update_speed(mario, tiles, pressed)
+ update_position(mario, tiles)
+ draw(mario, tiles, screen, pressed, images)
pygame.time.wait(28)
-def update_speed(mario, pressed):
- bounds = get_boundaries(mario.rect)
+def update_speed(mario, tiles, pressed):
+ bounds = get_boundaries(mario.rect, tiles)
x, y = mario.spd
x += 2 * ((D.e in pressed) - (D.w in pressed))
x = math.copysign(abs(x) - 1, x) if x else 0
@@ -2512,36 +2519,34 @@ TILES = [IMAGE.subsurface(pygame.Rect(x*16, for thresh, s in zip(MAX_SPEED, speed)])
-def update_position(mario):
- delta, old_p = P(0, 0), mario.rect.topleft
+def update_position(mario, tiles):
+ old_p, delta = mario.rect.topleft, P(0, 0)
larger_speed = max(abs(s) for s in mario.spd)
for _ in range(int(larger_speed)):
- mario.spd = stop_on_collision(mario.spd, get_boundaries(mario.rect))
- delta = P(*[s/larger_speed + dlt for s, dlt in zip(mario.spd, delta)])
- mario.rect.topleft = [sum(a) for a in zip(old_p, delta)]
+ mario.spd = stop_on_collision(mario.spd, get_boundaries(mario.rect, tiles))
+ delta = P(*[a + s/larger_speed for a, s in zip(delta, mario.spd)])
+ mario.rect.topleft = [sum(pair) for pair in zip(old_p, delta)]
-def get_boundaries(rect):
+def get_boundaries(rect, tiles):
deltas = {D.n: P(0, -1), D.e: P(1, 0), D.s: P(0, 1), D.w: P(-1, 0)}
- return {d for d, delta in deltas.items() if rect.move(delta).collidelist(FLOORS) != -1}
+ return {d for d, delta in deltas.items() if rect.move(delta).collidelist(tiles) != -1}
def stop_on_collision(spd, bounds):
return P(x=0 if (D.w in bounds and spd.x < 0) or (D.e in bounds and spd.x > 0) else spd.x,
y=0 if (D.n in bounds and spd.y < 0) or (D.s in bounds and spd.y > 0) else spd.y)
-def draw(screen, mario, pressed):
+def draw(mario, tiles, screen, pressed, images):
+ def get_frame_index():
+ if D.s not in get_boundaries(mario.rect, tiles):
+ return 4
+ return next(mario.frame_cycle) if {D.w, D.e} & pressed else 6
screen.fill((85, 168, 255))
- mario.facing_left = mario.spd.x < 0 if mario.spd.x else mario.facing_left
- screen.blit(FRAMES[get_frame_index(mario, pressed) + mario.facing_left*7], mario.rect)
- for rect in FLOORS:
- tile_index = 1 if {*rect.topleft} & {0, (SCR_SIDE-1)*RECT_SIDE} else 0
- screen.blit(TILES[tile_index], rect)
+ mario.facing_left = (D.w in pressed) if {D.e, D.w} & pressed else mario.facing_left
+ screen.blit(images[get_frame_index() + mario.facing_left*9], mario.rect)
+ for rect in tiles:
+ screen.blit(images[19 if {*rect.topleft} & {0, (SIZE-1)*16} else 18], rect)
pygame.display.flip()
-def get_frame_index(mario, pressed):
- if D.s not in get_boundaries(mario.rect):
- return 4
- return next(mario.running_cycle) if {D.w, D.e} & pressed else 6
-
if __name__ == '__main__':
main()