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. Version de nuit avec ciel étoilé et simulation d’ombre. 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é. Version basse résolution pour accélérer le tracer.
from kandinsky import set_pixel, fill_rect, color from ion import keydown from math import tan, cos, sin, atan, pi, ceil from random import choice, randint 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 shade(r, g, b, d): coeff = (1.6-atan(d/25-6))/(3.2) #coeff = 1 - 3*((d-100)/300)**2 + 2*((d-100)/300)**3 if 100 <= d <= 400 else 0 if d > 400 else 1 #coeff = 1 - 1/(exp((-d+200)/50) + 1) r, g, b = round(r*coeff), round(g*coeff), round(b*coeff) return color(r, g, b) def draw(): buffer = [] for k in range(108): delta = (k+1)//2*(-1)**k + k%2*108 - 54 angle = ANGLE + delta*5/9 d, hori, col = collision(X, Y, angle if angle%45 else angle+.05) d = round(d * COS(ANGLE-angle)) h = round(14000 / d) r, g, b = [light, dark][hori][col] buffer.append((160+3*delta, 111-h, 3, 2*h, shade(r, g, b, d))) fill_rect(0, 0, 320, 222, color(16, 16, 69)) for x, y in stars[ANGLE]: set_pixel(x, y, color(255, 255, 255)) for p in range(1, 112, 3): fill_rect(0, 111 + p, 320, 3, shade(139, 69, 19, 14000/p)) for x, y, dx, dy, c in buffer: fill_rect(x, y, dx, dy, c) del buffer 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) stars = {angle: [] for angle in range(-135, 181, 45)} for angle in list(stars): for _ in range(200): stars[angle].append((randint(0, 319), randint(0, 111))) 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, "@")