Génère un labyrinthe en 3D de manière aléatoire. Lorsqu’on trouve la sortie (mur vert), on accède à un nouveau labyrinthe aléatoire, et ce à l’infini. Commandes : flèches de gauche/droite pour tourner à gauche/droite de 45 degrés, flèche du haut/bas pour avancer/reculer, parenthèses pour les pas de côté.
from kandinsky import fill_rect, color from ion import keydown from math import tan, cos, sin, pi, ceil from random import choice def maze(wall): G = [[wall]*n for _ in range(n)] G[1][1] = G[1][2] = G[1][3] = "." order = [(0, 1, 2, 3), (0, 1, 3, 2), (0, 2, 1, 3), (0, 2, 3, 1), (0, 3, 1, 2), (0, 3, 2, 1), (1, 0, 2, 3), (1, 0, 3, 2), (1, 2, 0, 3), (1, 2, 3, 0), (1, 3, 0, 2), (1, 3, 2, 0), (2, 0, 1, 3), (2, 0, 3, 1), (2, 1, 0, 3), (2, 1, 3, 0), (2, 3, 0, 1), (2, 3, 1, 0), (3, 0, 1, 2), (3, 0, 2, 1), (3, 1, 0, 2), (3, 1, 2, 0), (3, 2, 0, 1), (3, 2, 1, 0)] stack = [(1, 3, 0)] iexit, jexit, tmax = None, None, 0 while stack: i, j, t = stack.pop() for k in choice(order): I, J = [(i+2, j), (i-2, j), (i, j+2), (i, j-2)][k] if 0 <= I < n and 0 <= J < n and G[I][J] != ".": G[I][J] = G[(I+i) // 2][(J+j) // 2] = "." if t > tmax and (I in (1, n-2) or J in (1, n-2)): tmax, iexit, jexit = t, I, J stack.append((I, J, t + 1)) G[1][0] = "S" if iexit == 1: G[0][jexit] = "E" elif iexit == n - 2: G[n-1][jexit] = "E" elif jexit == 1: G[iexit][0] = "E" else: G[iexit][n-1] = "E" return G def collision(X, Y, angle): if angle <= -180: angle += 360 elif angle > 180: angle -= 360 MIN = float("inf") if angle != 0 and angle != 180: IT = 1/TAN(angle) b = angle < 0 y0 = int(Y/100)*100 if b else ceil(Y/100)*100 x0 = X + (y0 - Y)*IT while True: i, j = y0//100 - 1*b, int(x0/100) if not 0 <= i < n or not 0 <= j < n or M[i][j] != ".": break y0 += 100*(-1)**b x0 += 100*IT*(-1)**b MIN, horizontal = dist(X, Y, x0, y0), True if 0 <= i < n and 0 <= j < n: col = M[i][j] if angle != 90 and angle != -90: T = TAN(angle) b = angle > 90 or angle < -90 x0 = int(X/100)*100 if b else ceil(X/100)*100 y0 = Y + (x0 - X)*T while True: i, j = int(y0/100), x0//100 - 1*b if not 0 <= i < n or not 0 <= j < n or M[i][j] != ".": break x0 += 100*(-1)**b y0 += 100*T*(-1)**b d = dist(X, Y, x0, y0) if d < MIN: MIN, horizontal = d, False if 0 <= i < n and 0 <= j < n: col = M[i][j] return MIN, horizontal, col def draw(): fill_rect(0, 0, 320, 222, color(135, 206, 235)) fill_rect(0, 111, 320, 111, color(139, 69, 19)) for k in range(320): delta = (k+1)//2*(-1)**k + k%2*320 - 160 angle = ANGLE + delta*3/16 d, hori, col = collision(X, Y, angle if angle%45 else angle+.05) h = round(14000 / (d * COS(ANGLE-angle))) r, g, b = [light, dark][hori][col] fill_rect(160+delta, 111-h, 1, 2*h, color(r, g, b)) def hmove(s): global X, Y, ANGLE, M, LVL if ANGLE % 90: return x, y = X - s*100*SIN(ANGLE), Y + s*100*COS(ANGLE) if M[int(y/100)][int(x/100)] == ".": X, Y = x, y draw() elif M[int(y/100)][int(x/100)] == "E": LVL += 1 M, X, Y, ANGLE = maze("#" if LVL % 5 else "$"), 150, 150, 0 draw() def rmove(s): global ANGLE ANGLE += s*45 if ANGLE <= -180 or ANGLE > 180: ANGLE -= s*360 draw() def vmove(s, c): global X, Y, ANGLE, M, LVL if ANGLE % 90: return x, y = X + s*100*COS(ANGLE), Y + s*100*SIN(ANGLE) if M[int(y/100)][int(x/100)] == ".": X, Y = x, y draw() elif M[int(y/100)][int(x/100)] == "E": LVL += 1 M, X, Y, ANGLE = maze(c if LVL % 5 else "$"), 150, 150, 0 draw() dist = lambda X, Y, x, y: ((X-x)**2 + (Y-y)**2)**.5 TAN, COS, SIN = lambda a: tan(a/180*pi), lambda a: cos(a/180*pi), lambda a: sin(a/180*pi) n = 15 # MUST BE ODD between 3 and 29 M, X, Y, ANGLE, LVL = maze("#"), 150, 150, 0, 1 light = dict(zip("#S$@E", [(229,229,229), (255,0,0), (255,215,0), (72,118,255), (0,238,0)])) dark = dict(zip("#S$@E", [(179,179,179), (205,0,0), (205,173,0), (58,95,205), (0,205,0)])) draw() while not keydown(5): if keydown(33): hmove(-1) if keydown(34): hmove(1) if keydown(0): rmove(-1) if keydown(3): rmove(1) if keydown(1): vmove(1, "#") if keydown(2): vmove(-1, "@")