Monday, 2 November 2026

Python Coding Challenge - Question with Answer (ID 031126)

 


Explanation:

Step 1: Understanding Complex Numbers

Python supports complex numbers, which consist of two parts:

- Real part – the number without j.

- Imaginary part – the number multiplied by j.

In our code:

z = 3 + 4j

Here:

- 3 is the real part.

- 4 is the imaginary part.

- j represents the imaginary unit.


Step 2: Understanding z.real

z.real

The .real attribute returns the real part of a complex number.

Output:

3.0

Step 3: Understanding z.imag

z.imag

The .imag attribute returns the imaginary coefficient.

Output:

4.0

Step 4: Understanding the Exponent Operator **

Now consider:

z.imag ** 2

Since z.imag = 4.0, Python calculates:

\[

4.0^2 = 16.0

\]

Step 5: Understanding the Complete Expression

z.real + z.imag ** 2

Python follows operator precedence, so exponentiation happens before addition.

\[

3.0 + (4.0)^2

\]

\[

= 3.0 + 16.0

\]

\[

= 19.0

\]

Step 6: Understanding print()

The print() function displays the final calculated value.


3. Final Output

Output: 19.0

19.0

Books: PYTHON LOOPS MASTERY


Python Coding challenge - Day 1302| What is the output of the following Python Code?

 




Code Explanation:

1. Defining a Generator Function
def process():

- Defines a function named process().
- This function contains yield statements, so Python treats it as a Generator Function.
- Unlike a normal function, a generator produces values one at a time instead of returning all values together.

2. First Yield Statement
    yield 10

- The yield keyword produces the value 10.
- The function pauses its execution at this point.
- Its current execution state is saved.
- When the generator is called again, execution resumes from where it stopped.

3. Second Yield Statement
    yield 20

- Produces the next value, which is 20.
- The generator pauses again.
- It remembers its current position and waits for the next request.

4. Return Statement
    return 30
- Ends the generator function.
- The value 30 is not yielded as a normal generator output.
- Instead, it becomes the value carried by the StopIteration exception.
- Once this statement executes, the generator is exhausted.
Important: yield produces values, whereas return terminates the generator.

5. Creating the Generator Object
g = process()

- Calls the generator function.
- Creates a generator object and stores it in g.
- The function body does not execute immediately.
- Execution starts only when we request a value using next().

6. Getting the First Value
print(next(g))
- next() starts executing the generator.
- Execution reaches yield 10.
- The generator pauses and returns 10.
- print() displays the value.
Output:
10

7. Getting the Second Value
print(next(g))
- Calls next() again on the same generator.
- Execution resumes after the first yield.
- It reaches yield 20.
- The generator pauses again and returns 20.
Output:
20


8. Handling Generator Exhaustion
print(next(g, "Finished"))

- Calls next() for the third time.
- The generator resumes after yield 20.
- It executes return 30 and terminates.
- Python raises StopIteration, carrying 30 as its value.
- However, next(g, "Finished") uses "Finished" as the default value when the generator is exhausted.
- Therefore, it prints "Finished" instead of raising an unhandled exception.
Output:
Finished


Final Output
Correct Answer
10
20
Finished

Python Coding challenge - Day 1301| What is the output of the following Python Code?

 

Code Explanation:

1. Defining the Outer Function

def multiplier(n):

- Defines a function named multiplier.
- It accepts one parameter n.
- This function will create another function that remembers the value of n.
- This is the foundation of a Closure in Python.

2. Defining the Inner Function
    def calculate(x):
- Defines an inner function named calculate.
- It accepts one parameter x.
- Since this function is defined inside multiplier(), it can access variables from the outer function.

3. Returning the Calculation
        return x * n
- Multiplies x by n.
- Here, x is a local parameter of calculate().
- n comes from the enclosing multiplier() function.
- Even after the outer function finishes executing, the inner function remembers the value of n.
Important: This ability to remember variables from an enclosing scope is called a closure.

4. Returning the Inner Function
    return calculate
- Returns the inner function itself, not its result.
- Notice that we use calculate, not calculate().
- This allows the returned function to be stored in another variable and called later.

5. Creating the Double Function
double = multiplier(2)
- Calls multiplier() with n = 2.
- The inner function calculate is returned.
- It remembers that n is 2.
- The returned function is stored in double.
So logically:

double(x) = x * 2

6. Creating the Triple Function
triple = multiplier(3)
- Calls multiplier() again, this time with n = 3.
- A new inner function is returned.
- This function remembers n = 3.
- It is stored in the variable triple.
So logically:
triple(x) = x * 3

7. Calling Both Functions
print(double(triple(4)))

Let's evaluate this expression step by step.

Step 1: Execute the innermost function:
triple(4)

Since triple remembers n = 3:

4 * 3 = 12

Step 2: Pass the result to double():
double(12)

Since double remembers n = 2:

12 * 2 = 24

Step 3: Print the final result:
print(24)


Final Output
24

Pydantic Settings: Manage Python Application Configuration Like a Pro

 



Pydantic Settings: Manage Python Application Configuration Like a Pro

SEO Title: Pydantic Settings in Python: Manage Environment Variables and Application Configuration

Meta Description: Learn how to use pydantic-settings in Python to manage environment variables, validate application configuration, load .env files, and build cleaner applications with practical examples.

Keywords: Pydantic Settings, Python environment variables, pydantic-settings tutorial, Python configuration management, Python backend development, Python libraries


Introduction

When building Python applications, configuration management is an important part of writing clean and maintainable code.

You might have different configuration values for:

  • Application names

  • Database connections

  • API keys

  • Debug mode

  • Server ports

  • External service URLs

A common beginner approach is to hardcode these values directly into Python files.

APP_NAME = "CLCODING"
DEBUG = True
PORT = 8000

This works for small projects, but it becomes difficult to maintain as your application grows.

What if you need different configurations for development, testing, and production?

This is where Pydantic Settings becomes useful.

What is Pydantic Settings?

pydantic-settings is a Python library that helps you manage application configuration using environment variables, .env files, and other settings sources.

It builds on Pydantic's validation capabilities, allowing you to define configuration using Python type hints.

Instead of manually reading and converting environment variables, you can define a settings class and let the library handle loading and validation.

Key benefits:

  1. Load configuration from environment variables.

  2. Validate configuration values using Python types.

  3. Support .env files.

  4. Organize application settings in one place.

  5. Reduce hardcoded configuration.

  6. Make applications easier to deploy and maintain.

Installation

Install the package using pip:

pip install pydantic-settings

Your First Pydantic Settings Example

Let's create a simple application configuration.

import os
from pydantic_settings import BaseSettings, SettingsConfigDict

# Set environment variables for demonstration
os.environ["APP_NAME"] = "CLCODING"
os.environ["APP_DEBUG"] = "true"
os.environ["APP_PORT"] = "9000"


class Settings(BaseSettings):

    model_config = SettingsConfigDict(
        env_prefix="APP_"
    )

    name: str = "My Python App"
    debug: bool = False
    port: int = 8000


settings = Settings()

print("App:", settings.name)
print("Debug:", settings.debug)
print("Port:", settings.port)

Expected Output

App: CLCODING
Debug: True
Port: 9000

How does this code work?

1. Import the required classes

from pydantic_settings import BaseSettings, SettingsConfigDict

BaseSettings provides the settings functionality, while SettingsConfigDict lets you configure how settings are loaded.

2. Define environment variables

os.environ["APP_NAME"] = "CLCODING"
os.environ["APP_DEBUG"] = "true"
os.environ["APP_PORT"] = "9000"

These simulate environment variables that would normally be configured outside your application.

3. Create a Settings class

class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_prefix="APP_")

    name: str = "My Python App"
    debug: bool = False
    port: int = 8000

The APP_ prefix tells Pydantic Settings to look for:

Python fieldEnvironment variable
nameAPP_NAME
debugAPP_DEBUG
portAPP_PORT

4. Create the settings object

settings = Settings()

The library reads matching environment variables and validates their values against the declared types.

For example, "9000" becomes the integer 9000, and "true" becomes the boolean True.

If an environment variable is not provided, the field's default value is used.

Using a .env File

For larger projects, you may prefer to keep configuration in a .env file during local development.

Create a file named .env:

APP_NAME=CLCODING
APP_DEBUG=true
APP_PORT=9000

Now update your Python code:

from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):

    model_config = SettingsConfigDict(
        env_prefix="APP_",
        env_file=".env",
        env_file_encoding="utf-8"
    )

    name: str = "My Python App"
    debug: bool = False
    port: int = 8000


settings = Settings()

print(settings.model_dump())

Expected Output

{
    'name': 'CLCODING',
    'debug': True,
    'port': 9000
}

Important: Install python-dotenv when using dotenv file support:

pip install python-dotenv

Environment variables take priority over values loaded from a .env file. Keep real credentials out of source control and add .env to your .gitignore.

Practical Example: Database Configuration

You can also use Pydantic Settings to manage database configuration.

from pydantic_settings import BaseSettings, SettingsConfigDict


class DatabaseSettings(BaseSettings):

    model_config = SettingsConfigDict(
        env_prefix="DB_"
    )

    host: str = "localhost"
    port: int = 5432
    name: str = "my_database"
    user: str = "admin"


db = DatabaseSettings()

print("Database Host:", db.host)
print("Database Port:", db.port)
print("Database Name:", db.name)
print("Database User:", db.user)

You can override these values through environment variables without changing your Python source code.

For example:

DB_HOST=production.example.com
DB_PORT=5432
DB_NAME=production_db
DB_USER=app_user

This makes it easier to use the same application code across multiple environments.

Where Can You Use Pydantic Settings?

Pydantic Settings is particularly useful for:

1. FastAPI Applications

Manage application configuration, database connections, and external service settings.

2. Machine Learning Projects

Configure model paths, experiment parameters, API endpoints, and storage locations.

3. AI Applications

Manage LLM provider settings, API endpoints, model names, and service configuration.

4. Backend Development

Separate application configuration from business logic.

5. Production Deployment

Provide environment-specific configuration without changing application source code.

Best Practices

  • Avoid hardcoding API keys and passwords.

  • Use environment variables for deployment configuration.

  • Keep local .env files out of Git.

  • Define appropriate Python types for settings.

  • Use sensible defaults only for non-sensitive configuration.

  • Validate required settings instead of silently accepting missing values.

  • Use a secrets manager for sensitive production credentials.

Remember: Pydantic Settings helps load and validate configuration. It does not automatically encrypt or secure your secrets.

Conclusion

Configuration management is an essential part of professional Python development.

pydantic-settings makes this process easier by combining environment variable loading, type validation, and structured configuration into a simple Python interface.

If you are building APIs, backend services, machine learning applications, or AI-powered tools, this library is worth learning.

Try it in your next Python project!

Useful Resources

Follow CLCODING for more Python tutorials, libraries, coding tips, and practical developer resources.

From Code to Light: A Beautiful Diwali Diya Animation in Python

 


from __future__ import annotations

import math import time from dataclasses import dataclass from typing import Optional, Tuple import numpy as np TAU = 2.0 * math.pi # ============================================================================= # 1. CONFIGURATION (everything here can also be set from the command line) # ============================================================================= @dataclass class Config: fps: float = 30.0 # target frames per second speed: float = 1.0 # global animation speed (also speeds up the intro) intro: float = 1.0 # multiplies the length of the intro stages (1-4) density: float = 1.0 # particle density multiplier glow: float = 1.0 # strength of the surrounding ambient glow accent: float = 1.0 # amount of cyan / electric-blue accents (0 = none) cols: int = 0 # canvas width in characters (0 = whole terminal) rows: int = 0 # canvas height in lines (0 = whole terminal) color_mode: str = "auto" # auto | truecolor | 256 seed: Optional[int] = None # fixed random seed for a repeatable animation duration: float = 0.0 # stop automatically after N seconds (0 = never) # Colour ramps: (heat 0..1, (r, g, b)). Edit these to re-colour the whole piece. FIRE_STOPS = [ (0.00, (35, 4, 28)), # deep plum (0.12, (125, 8, 38)), # ruby red (0.27, (220, 30, 42)), # vivid crimson (0.42, (255, 76, 35)), # coral (0.58, (255, 137, 25)), # bright orange (0.73, (255, 194, 45)), # amber (0.88, (255, 235, 105)), # golden yellow (1.00, (255, 255, 230)), # warm-white core ] ACCENT_STOPS = [ (0.00, (35, 12, 110)), # royal purple (0.24, (88, 35, 210)), # violet (0.48, (0, 115, 255)), # electric blue (0.70, (0, 220, 220)), # turquoise (0.86, (70, 255, 170)), # emerald (1.00, (235, 255, 255)), # icy highlight ] FIRE_STEPS = 48 # number of distinct fire colours ACCENT_STEPS = 20 # number of distinct accent colours # Glyphs. Index into GLYPHS is what the frame buffers store (0 = blank). GLYPHS = " .,:;!|/\\-_=+*xX#%@" LEVEL_GROUPS = [".,", ":;", "+*", "xX#", "%@"] # very low .. maximum intensity LEVEL_EDGES = np.array([0.07, 0.20, 0.36, 0.58, 0.80]) # heat thresholds between groups # ============================================================================= # 2. SMALL HELPERS # ============================================================================= def smoothstep(e0, e1, x): t = np.clip((x - e0) / (e1 - e0), 0.0, 1.0) return t * t * (3.0 - 2.0 * t) def flow(a, b, t): """Cheap smooth pseudo-noise in [-1, 1] built from travelling sine waves.""" return (np.sin(a * 1.7 + b * 1.1 + t * 1.9) + np.sin(a * 3.3 - b * 2.1 - t * 2.7 + 1.3) + np.sin(a * 5.9 + b * 3.7 + t * 3.9 + 2.1)) * (1.0 / 3.0) def build_ramp(stops, steps): pos = np.array([s[0] for s in stops], dtype=np.float64) rgb = np.array([s[1] for s in stops], dtype=np.float64) xs = np.linspace(0.0, 1.0, steps) cols = [np.interp(xs, pos, rgb[:, c]) for c in range(3)] return np.stack(cols, axis=1).round().astype(np.uint8) def rgb_to_xterm256(r: int, g: int, b: int) -> int: """Nearest xterm-256 colour (6x6x6 cube or greyscale ramp).""" levels = (0, 95, 135, 175, 215, 255) def nearest(v): return min(range(6), key=lambda i: abs(levels[i] - v)) ri, gi_, bi = nearest(r), nearest(g), nearest(b) cube = (levels[ri], levels[gi_], levels[bi]) cube_idx = 16 + 36 * ri + 6 * gi_ + bi gray_i = min(23, max(0, int(round((((r + g + b) / 3.0) - 8) / 10.0)))) gv = 8 + 10 * gray_i d_cube = sum((a - c) ** 2 for a, c in zip((r, g, b), cube)) d_gray = sum((a - gv) ** 2 for a in (r, g, b)) return 232 + gray_i if d_gray < d_cube else cube_idx FIRE_RAMP = build_ramp(FIRE_STOPS, FIRE_STEPS) ACCENT_RAMP = build_ramp(ACCENT_STOPS, ACCENT_STEPS) COLOR_TABLE = np.vstack([np.zeros((1, 3), np.uint8), FIRE_RAMP, ACCENT_RAMP]) ACCENT_BASE = 1 + FIRE_STEPS # first accent index in COLOR_TABLE def _glyph(c: str) -> int: return GLYPHS.index(c) GROUP_IDX = np.array( [[_glyph(g[i % len(g)]) for i in range(4)] for g in LEVEL_GROUPS], dtype=np.int32 ) # shape (5 intensity levels, 4 interchangeable variants) def shade(h, k, accent): """Heat (float), variant (int 0-3) and accent flag arrays -> (glyph, colour) index arrays.""" lvl = np.searchsorted(LEVEL_EDGES, h, side="right") # 0 = blank, 1..5 glyph = np.where(lvl > 0, GROUP_IDX[np.maximum(lvl - 1, 0), k], 0) hc = np.clip(h, 0.0, 1.0) fire = 1 + (hc * (FIRE_STEPS - 1) + 0.5).astype(np.int32) acc = ACCENT_BASE + (np.clip(h * 1.15, 0.0, 1.0) * (ACCENT_STEPS - 1) + 0.5).astype(np.int32) col = np.where(accent, acc, fire) return glyph.astype(np.uint8), col.astype(np.uint8) # ============================================================================= # 4. DIYA GEOMETRY # ============================================================================= class DiyaShape: """ Static geometry of the lamp, evaluated on the character grid. Coordinates are "normalised": X right, Y up, 1.0 = half the canvas height, and one terminal column counts as half a row so shapes keep their true proportions. """ Y_RIM = -0.20 # height of the rim ellipse centre R = 0.78 # half width of the bowl D = 0.46 # depth of the bowl below the rim P = 2.10 # super-ellipse power of the bowl profile RIM_B = 0.10 # half height of the rim ellipse (seen slightly from above) FOOT_TOP = -0.64 FOOT_BOT = -0.77 Y_WICK = Y_RIM + 0.02 def __init__(self, W: int, H: int, rng: np.random.Generator): self.W, self.H = W, H self.unit = max(4.0, min(H / 2.0, W / 4.0)) * 0.97 self.cx = (W - 1) / 2.0 self.cy = (H - 1) / 2.0 xs = (np.arange(W) - self.cx) * 0.5 / self.unit ys = (self.cy - np.arange(H)) / self.unit self.X, self.Y = np.meshgrid(xs, ys) # per-cell random constants (visibility, glyph variants, twinkle phases ...) self.r1 = rng.random((H, W)) self.r2 = rng.random((H, W)) self.r3 = rng.random((H, W)) self.phase = rng.uniform(0.0, TAU, (H, W)) self.rate = rng.uniform(1.5, 7.0, (H, W)) # glyph substitution speed self.rate2 = rng.uniform(0.7, 3.2, (H, W)) # twinkle speed inside, heat, oil, _ = self.evaluate(self.X, self.Y) self.bowl_in, self.oil = inside, oil self._build_edges(inside) # outline cells are drawn dark red / dark orange, the rest keeps its lighting self.bowl_heat = np.where(self.edge, 0.24 + 0.36 * heat, heat) * inside # soft radial illumination around the flame self.glow = np.exp(-((self.X / 0.80) ** 2 + ((self.Y - 0.10) / 0.85) ** 2)) # how late each cell is revealed while the diya forms (centre first, ragged edge) dist = np.hypot(self.X * 0.85, self.Y + 0.15) / 1.05 self.rv = 0.65 * np.clip(dist, 0.0, 1.3) + 0.35 * self.r1 # ---- analytic description of the lamp ------------------------------ def evaluate(self, X, Y): """Return (inside, static heat, oil mask, sampling weight) for arbitrary coordinates.""" R, yr = self.R, self.Y_RIM ax = np.abs(X) below = Y <= yr g = (ax / R) ** self.P + (np.maximum(yr - Y, 0.0) / self.D) ** 2 body = below & (g <= 1.0) ell = (ax / (0.98 * R)) ** 1.8 + ((Y - yr) / self.RIM_B) ** 2 rim = ell <= 1.0 foot_t = np.clip((self.FOOT_TOP - Y) / (self.FOOT_TOP - self.FOOT_BOT), 0.0, 1.0) foot_hw = 0.22 + 0.15 * foot_t ** 1.6 foot = (Y <= self.FOOT_TOP) & (Y >= self.FOOT_BOT) & (ax <= foot_hw) inside = body | rim | foot # lighting of the bowl body: brighter near the rim, highlights on the shoulders ybot = yr - self.D v = np.clip((Y - ybot) / self.D, 0.0, 1.0) heat = 0.40 + 0.34 * v ** 1.2 + 0.12 * v * np.exp(-(X / 0.5) ** 2) hl1 = np.exp(-(((X + 0.42 * R) / 0.22) ** 2 + ((Y - (yr - 0.14)) / 0.06) ** 2)) hl2 = np.exp(-(((X - 0.50 * R) / 0.15) ** 2 + ((Y - (yr - 0.20)) / 0.045) ** 2)) heat = heat + 0.40 * hl1 + 0.18 * hl2 # decorative band: a row of glowing pearls and a darker line above it yb = yr - 0.21 pearl = (np.abs(Y - yb) < 0.026) & (np.abs((X / 0.105) % 1.0 - 0.5) < 0.21) & (g < 0.72) heat = np.where(pearl, 0.92, heat) line = (np.abs(Y - (yb + 0.075)) < 0.011) & (g < 0.85) heat = np.where(line, heat * 0.55, heat) # foot foot_h = 0.30 + 0.28 * (1.0 - foot_t) heat = np.where(foot & ~body, foot_h, heat) # rim ellipse: bright front lip, softer back lip, glowing oil inside ring = rim & (ell > 0.60) heat = np.where(ring, np.where(below, 0.90, 0.55), heat) oil = rim & (ell <= 0.60) heat = np.where(oil, 0.80 + 0.18 * np.exp(-(X / 0.2) ** 2), heat) edge_b = np.exp(-((g - 0.93) / 0.14) ** 2) * below edge_e = np.exp(-((ell - 0.85) / 0.20) ** 2) weight = inside * (0.30 + 0.70 * np.maximum(np.maximum(edge_b, edge_e), foot * 0.6)) return inside, heat * inside, oil, weight def _build_edges(self, inside): """Outline cells and the glyph ( / \\ | - _ ) that follows the outline direction.""" pad = np.pad(inside, 1) eroded = inside & pad[:-2, 1:-1] & pad[2:, 1:-1] & pad[1:-1, :-2] & pad[1:-1, 2:] self.edge = inside & ~eroded def blur(a): p = np.pad(a, 1) return (4 * a + 2 * (p[:-2, 1:-1] + p[2:, 1:-1] + p[1:-1, :-2] + p[1:-1, 2:]) + (p[:-2, :-2] + p[:-2, 2:] + p[2:, :-2] + p[2:, 2:])) / 16.0 s = blur(blur(inside.astype(np.float64))) gy, gx = np.gradient(s) # d/drow, d/dcol th = np.arctan2(2.0 * gx, gy) % math.pi # direction of the outline in screen space glyph = np.full(inside.shape, _glyph("|"), np.uint8) top = gy > 0 # inside lies below -> upper outline horiz = (th < math.pi / 8) | (th >= 7 * math.pi / 8) slash = (th >= math.pi / 8) & (th < 3 * math.pi / 8) back = (th >= 5 * math.pi / 8) & (th < 7 * math.pi / 8) glyph[slash] = _glyph("/") glyph[back] = _glyph("\\") glyph[horiz & top] = _glyph("-") glyph[horiz & ~top] = _glyph("_") self.edge_glyph = glyph self.edge_sel = self.edge & (self.r3 < 0.88) # a few gaps keep it organic # ---- sampling / mapping -------------------------------------------- def to_cell(self, X, Y): c = np.rint(self.cx + 2.0 * self.unit * X).astype(np.int64) r = np.rint(self.cy - self.unit * Y).astype(np.int64) return r, c def sample_bowl(self, n: int, rng: np.random.Generator): """n home positions on the lamp (outline weighted) plus their static heat.""" xs, ys, hs = [], [], [] got = 0 while got < n: m = (n - got) * 4 + 64 X = rng.uniform(-self.R * 1.02, self.R * 1.02, m) Y = rng.uniform(self.FOOT_BOT, self.Y_RIM + self.RIM_B, m) _, heat, _, w = self.evaluate(X, Y) keep = rng.random(m) < w xs.append(X[keep]) ys.append(Y[keep]) hs.append(heat[keep]) got += int(keep.sum()) return np.concatenate(xs)[:n], np.concatenate(ys)[:n], np.concatenate(hs)[:n] # ============================================================================= # 5. FLAME # ============================================================================= class FireSimulation: """Procedural, continuously changing flame (shape, sway, flicker, heat).""" H0 = 0.98 # full flame height (normalised units) W0 = 0.20 # full flame half width def __init__(self, shape: DiyaShape): self.shape = shape self.y0 = shape.Y_WICK self.burst = 0.0 # set by the controller for "glowing bursts" def height(self, t, grow): f = (1.0 + 0.055 * math.sin(TAU * 1.9 * t + 0.5) + 0.040 * math.sin(TAU * 3.7 * t + 1.7) + 0.025 * math.sin(TAU * 7.3 * t + 0.2)) return self.H0 * grow * f * (1.0 + 0.10 * self.burst) def sway(self, s, t, grow): sc = np.clip(s, 0.0, 1.0) return grow * sc ** 1.5 * (0.050 * np.sin(TAU * 0.55 * t + 2.4 * sc) + 0.032 * np.sin(TAU * 1.35 * t + 4.3 * sc + 1.0) + 0.016 * np.sin(TAU * 3.10 * t + 7.0 * sc + 2.0)) def half_width(self, s, t, grow): sc = np.clip(s, 0.0, 1.0) body = np.maximum(np.sin(math.pi * sc ** 0.62), 0.0) ** 0.9 wob = 1.0 + 0.14 * flow(sc * 3.0, 0.0, t * 2.3) return np.maximum(self.W0 * grow ** 0.8 * body * wob, 0.010) @staticmethod def profile(u, s): """Flame heat for a point at |u| half-widths from the centre line, s along its height.""" sc = np.clip(s, 0.0, 1.0) body = np.exp(-(u / 0.98) ** 4) # defined flame edge halo = np.exp(-(u / 1.7) ** 2) # soft red-orange fringe core = np.exp(-(u / 0.50) ** 2) * (1.0 - 0.75 * sc) return (0.74 * body + 0.20 * halo + 0.34 * core) * (1.0 - 0.25 * sc ** 2) def place(self, s, u, t, grow): """Map flame coordinates (s along height, u across width) to normalised X, Y.""" Y = self.y0 + s * self.height(t, grow) X = self.sway(s, t, grow) + u * self.half_width(s, t, grow) return X, Y def field(self, t, grow, energy): """Flame heat (0..1) for every character cell.""" sh = self.shape X, Y = sh.X, sh.Y hf = max(self.height(t, grow), 1e-3) s = (Y - self.y0) / hf sc = np.clip(s, 0.0, 1.0) w = self.half_width(sc, t, grow) u = np.abs(X - self.sway(sc, t, grow)) / w u = u * (1.0 + 0.22 * flow(X * 6.0, Y * 6.5 - t * 2.6, t * 1.3)) heat = self.profile(u, sc) env = smoothstep(-0.03, 0.10, s) * (1.0 - smoothstep(0.93, 1.12, s)) bands = 0.88 + 0.12 * flow(X * 4.5, Y * 9.0 - t * 4.2, t * 0.7) return np.clip(heat * env * bands * energy, 0.0, 1.0) # ============================================================================= # 6. PARTICLES # ============================================================================= @dataclass class Particle: """Snapshot of one particle. ParticleSystem stores the same fields as numpy columns (struct-of-arrays) so thousands of them can be updated per frame.""" x: float = 0.0 y: float = 0.0 vx: float = 0.0 vy: float = 0.0 intensity: float = 1.0 life: float = 1.0 max_life: float = 1.0 flicker: float = 4.0 # Hz accent: bool = False char: str = " " color: Tuple[int, int, int] = (0, 0, 0) class Controls: """Everything the animation stages drive (see AnimationController.controls).""" stage = 1 gain = 0.0 # fraction of particles that are visible radius = 0.05 # size of the initial spark cloud structure = 0.0 # how far particles have moved to their place in the diya flame_grow = 0.12 # size of the flame (0..1) field_gain = 0.0 # how much of the solid, dense diya texture is revealed glow_gain = 0.0 # ambient radial glow accent_gain = 0.0 # cyan / blue accents allowed core_heat = 0.55 # temperature of the middle of the spark cloud flick_depth = 0.95 # how strongly particles blink on/off alive = False class ParticleSystem: """ Three kinds of "former" particles (bowl, flame, ambient) start as a spark cloud and drift to their place in the diya, plus a pool of free-flying sparks that rise from the flame. """ C0 = (0.0, 0.0) # centre of the first sparks (centre of the terminal) def __init__(self, shape: DiyaShape, fire: FireSimulation, cfg: Config, rng: np.random.Generator): self.shape, self.fire, self.cfg, self.rng = shape, fire, cfg, rng cells = shape.W * shape.H d = max(cfg.density, 0.05) nA, nB, nC = int(0.14 * cells * d), int(0.10 * cells * d), int(0.30 * cells * d) nS = int(0.045 * cells * d) + 24 n = nA + nB + nC self.n, self.nS = n, nS self.sl_bowl = slice(0, nA) self.sl_flame = slice(nA, nA + nB) self.sl_amb = slice(nA + nB, n) # --- common properties of the formers scale = np.array([0.75, 0.55]) self.sc_dir = np.clip(rng.standard_normal((n, 2)), -2.4, 2.4) * scale self.appear = rng.random(n) self.delay = rng.random(n) self.intensity = 0.55 + 0.45 * rng.random(n) self.flick = rng.uniform(1.5, 9.0, n) self.phase = rng.uniform(0.0, TAU, n) self.accent = np.zeros(n, bool) # --- bowl particles: sampled on the lamp self.bowl_hx, self.bowl_hy, self.bowl_heat = shape.sample_bowl(nA, rng) self.accent[self.sl_bowl] = rng.random(nA) < 0.02 * cfg.accent # --- flame particles: coordinates inside the (moving) flame s = np.empty(0) while s.size < nB: c = rng.random(nB * 3 + 32) keep = rng.random(c.size) < np.sqrt(np.maximum(np.sin(math.pi * c ** 0.62), 0.0)) s = np.concatenate([s, c[keep]]) self.fs = s[:nB] self.fu = np.clip(rng.normal(0.0, 0.55, nB), -1.5, 1.5) self.accent[self.sl_flame] = (np.abs(self.fu) > 1.0) & (rng.random(nB) < 0.35 * cfg.accent) # --- ambient particles: a halo around the flame with different behaviours r = np.abs(rng.standard_normal(nC)) * 0.44 a = rng.uniform(0.0, TAU, nC) self.amb_hx = r * np.cos(a) * 1.35 self.amb_hy = 0.06 + r * np.sin(a) * 0.95 self.beh = rng.integers(0, 4, nC) # 0 still, 1 drifts sideways, 2 rises, 3 pulses self.beh_rate = rng.uniform(0.08, 0.35, nC) self.accent[self.sl_amb] = rng.random(nC) < 0.09 * cfg.accent self._hx = np.empty(n) self._hy = np.empty(n) self._home = np.empty(n) self._hx[self.sl_bowl] = self.bowl_hx self._hy[self.sl_bowl] = self.bowl_hy # --- free-flying sparks self.s_x = np.zeros(nS) self.s_y = np.zeros(nS) self.s_vx = np.zeros(nS) self.s_vy = np.zeros(nS) self.s_life = np.zeros(nS) self.s_max = np.ones(nS) self.s_int = np.ones(nS) self.s_acc = np.zeros(nS, bool) self.s_rate = rng.uniform(2.0, 8.0, nS) self.s_ph = rng.uniform(0.0, TAU, nS) self.spark_rate = nS / 1.8 # emissions per second at full flame # ---- sparks --------------------------------------------------------- def emit_sparks(self, count: int, t: float, grow: float, strength: float = 1.0): free = np.flatnonzero(self.s_life <= 0.0)[:count] m = free.size if m == 0: return rng = self.rng s = rng.uniform(0.05, 0.70, m) u = rng.normal(0.0, 0.55, m) x, y = self.fire.place(s, u, t, grow) life = rng.uniform(0.9, 2.8, m) * (0.8 + 0.4 * strength) self.s_x[free], self.s_y[free] = x, y self.s_vx[free] = rng.normal(0.0, 0.035 * strength, m) self.s_vy[free] = rng.uniform(0.10, 0.50, m) * strength self.s_life[free] = life self.s_max[free] = life self.s_int[free] = rng.uniform(0.75, 1.0, m) self.s_acc[free] = rng.random(m) < 0.22 * self.cfg.accent def _update_sparks(self, t, dt, ctl): self.s_life -= dt np.maximum(self.s_life, -1.0, out=self.s_life) self.s_x += (self.s_vx + 0.045 * np.sin(TAU * self.s_rate * t + self.s_ph)) * dt self.s_y += self.s_vy * dt self.s_vx *= max(0.0, 1.0 - 0.25 * dt) if ctl.stage >= 4 and ctl.flame_grow > 0.3: n = self.rng.poisson(self.spark_rate * ctl.flame_grow ** 2 * dt) if n: self.emit_sparks(int(n), t, ctl.flame_grow) def snapshot(self, i: int) -> Particle: """Return spark i as a Particle record (handy for debugging / extensions).""" life = float(self.s_life[i]) h = float(self.s_int[i] * max(life, 0.0) / self.s_max[i]) ch, col = shade(np.array([h]), np.zeros(1, np.int64), np.array([bool(self.s_acc[i])])) r, g, b = COLOR_TABLE[int(col[0])].tolist() return Particle(float(self.s_x[i]), float(self.s_y[i]), float(self.s_vx[i]), float(self.s_vy[i]), float(self.s_int[i]), life, float(self.s_max[i]), float(self.s_rate[i]), bool(self.s_acc[i]), GLYPHS[int(ch[0])], (r, g, b)) # ---- per-frame state ------------------------------------------------- def render(self, t, dt, ctl: Controls): """Advance the simulation and return per-cell (heat, glyph variant, accent) layers.""" sh = self.shape H, W = sh.H, sh.W heat_buf = np.zeros((H, W)) var_buf = np.zeros((H, W), np.int64) acc_buf = np.zeros((H, W), bool) if ctl.stage < 2: return heat_buf, var_buf, acc_buf self._update_sparks(t, dt, ctl) n = self.n k = smoothstep(0.0, 1.0, np.clip((ctl.structure - 0.6 * self.delay) / 0.4, 0.0, 1.0)) ph = self.phase # cloud ("scatter") positions and heat sx = self.C0[0] + self.sc_dir[:, 0] * ctl.radius + 0.006 * np.sin(TAU * 0.7 * t + ph) sy = self.C0[1] + self.sc_dir[:, 1] * ctl.radius + 0.006 * np.cos(TAU * 0.6 * t + ph) d = np.hypot(sx - self.C0[0], (sy - self.C0[1]) * 1.3) scat = (0.10 + ctl.core_heat * np.exp(-(d / (0.22 + 0.55 * ctl.radius)) ** 2)) * self.intensity # home positions / heat hx, hy, home = self._hx, self._hy, self._home b, f, a = self.sl_bowl, self.sl_flame, self.sl_amb wave_x = 0.010 * np.sin(TAU * 0.31 * t + 3.1 * self.bowl_hy + ph[b]) hx[b] = self.bowl_hx + wave_x hy[b] = self.bowl_hy + 0.006 * np.cos(TAU * 0.27 * t + ph[b]) home[b] = self.bowl_heat * (0.80 + 0.20 * np.sin(TAU * self.flick[b] * 0.4 * t + ph[b])) fx, fy = self.fire.place(self.fs, self.fu, t, ctl.flame_grow) hx[f], hy[f] = fx, fy home[f] = self.fire.profile(np.abs(self.fu), self.fs) * (0.80 + 0.20 * self.intensity[f]) pa, beh, br = ph[a], self.beh, self.beh_rate rise_ph = (t * br * 0.5 + pa / TAU) % 1.0 drift = np.where(beh == 1, 0.07 * np.sin(TAU * br * t + pa), 0.0) rise = np.where(beh == 2, rise_ph * 0.55, 0.0) ax = self.amb_hx + 0.014 * np.sin(TAU * 0.31 * t + 3.1 * self.amb_hy + pa) + drift ay = self.amb_hy + 0.011 * np.cos(TAU * 0.27 * t + 2.3 * self.amb_hx + pa) + rise hx[a], hy[a] = ax, ay illum = np.exp(-((ax / 0.95) ** 2 + ((ay - 0.12) / 0.90) ** 2)) mod = np.ones(a.stop - a.start) mod = np.where(beh == 2, (1.0 - rise_ph) ** 0.7 * smoothstep(0.0, 0.06, rise_ph), mod) mod = np.where(beh == 3, 0.25 + 0.75 * np.maximum(0.0, np.sin(TAU * br * t + pa)) ** 3, mod) home[a] = (0.14 + 0.52 * illum) * self.intensity[a] * mod * (0.4 + 0.6 * ctl.glow_gain) # blend cloud -> home, then apply appear-over-time and on/off flickering x = sx * (1.0 - k) + hx * k y = sy * (1.0 - k) + hy * k heat = scat * (1.0 - k) + home * k vis = np.clip((ctl.gain - self.appear) * 20.0, 0.0, 1.0) raw = 0.5 + 0.5 * np.sin(TAU * self.flick * t + ph) fl = (1.0 - ctl.flick_depth) + ctl.flick_depth * smoothstep(0.15, 0.40, raw) heat = heat * vis * fl accent = self.accent & (ctl.accent_gain > 0.5) var = ((t * self.flick * 0.35 + ph).astype(np.int64)) & 3 # sparks alive = self.s_life > 0.0 shp = np.clip(self.s_life / self.s_max, 0.0, 1.0) ** 0.85 sheat = self.s_int * shp * (0.82 + 0.18 * np.sin(TAU * self.s_rate * t + self.s_ph)) * alive svar = ((t * self.s_rate * 0.5 + self.s_ph).astype(np.int64)) & 3 X = np.concatenate([x, self.s_x]) Y = np.concatenate([y, self.s_y]) Hh = np.concatenate([heat, sheat]) V = np.concatenate([var, svar]) A = np.concatenate([accent, self.s_acc]) r, c = sh.to_cell(X, Y) ok = (Hh > LEVEL_EDGES[0]) & (r >= 0) & (r < H) & (c >= 0) & (c < W) r, c, Hh, V, A = r[ok], c[ok], Hh[ok], V[ok], A[ok] order = np.argsort(Hh) # hottest particle written last wins the cell r, c = r[order], c[order] heat_buf[r, c] = Hh[order] var_buf[r, c] = V[order] acc_buf[r, c] = A[order] return heat_buf, var_buf, acc_buf # ============================================================================= # 7. ANIMATION CONTROLLER # ============================================================================= class AnimationController: """Runs the five stages and composes flame, lamp, glow and particles into one frame.""" # end of each stage in seconds at speed 1 / intro 1: # 1 darkness, 2 first sparks, 3 expansion, 4 formation, then 5 living artwork STAGE_ENDS = (1.0, 4.0, 8.5, 15.0) def __init__(self, cfg: Config, W: int, H: int): self.cfg = cfg self.rng = np.random.default_rng(cfg.seed) self.bounds = [e * max(cfg.intro, 0.05) for e in self.STAGE_ENDS] self.burst = 0.0 self.resize(W, H) def resize(self, W: int, H: int): self.W, self.H = W, H self.shape = DiyaShape(W, H, self.rng) self.fire = FireSimulation(self.shape) self.particles = ParticleSystem(self.shape, self.fire, self.cfg, self.rng) # ---- stage logic ---------------------------------------------------- def controls(self, t: float) -> Controls: b0, b1, b2, b3 = self.bounds c = Controls() if t < b0: # stage 1: darkness return c if t < b1: # stage 2: first sparks e = smoothstep(0.0, 1.0, (t - b0) / (b1 - b0)) c.stage, c.gain = 2, 0.002 + 0.028 * e c.radius, c.core_heat = 0.05 + 0.10 * e, 0.50 + 0.12 * e elif t < b2: # stage 3: particle expansion p = (t - b1) / (b2 - b1) e = smoothstep(0.0, 1.0, p) c.stage, c.gain = 3, 0.03 + 0.72 * p ** 1.5 c.radius, c.structure = 0.15 + 0.85 * e, 0.34 * e c.core_heat = 0.62 + 0.30 * e c.glow_gain = 0.30 * smoothstep(0.4, 1.0, p) c.accent_gain, c.flick_depth = p, 0.95 - 0.10 * p elif t < b3: # stage 4: diya formation p = (t - b2) / (b3 - b2) e = smoothstep(0.0, 1.0, p) c.stage, c.gain = 4, 0.75 + 0.25 * e c.radius, c.structure = 1.0, 0.34 + 0.80 * e c.flame_grow = 0.12 + 0.88 * smoothstep(0.05, 0.95, p) c.field_gain = smoothstep(0.25, 1.0, p) c.glow_gain, c.accent_gain = 0.30 + 0.70 * e, 1.0 c.core_heat, c.flick_depth = 0.95, 0.85 - 0.50 * e else: # stage 5: living artwork c.stage, c.gain, c.radius, c.structure = 5, 1.0, 1.0, 1.14 c.flame_grow, c.field_gain, c.glow_gain, c.accent_gain = 1.0, 1.0, 1.0, 1.0 c.core_heat, c.flick_depth, c.alive = 0.95, 0.35, True return c def _tick_bursts(self, t, dt, ctl): self.burst *= math.exp(-dt / 0.55) if ctl.alive and self.rng.random() < 0.30 * dt: # occasional glowing burst self.burst = 1.0 self.particles.emit_sparks(max(8, int(0.10 * self.particles.nS)), t, ctl.flame_grow, strength=1.5) self.fire.burst = self.burst # ---- composition ------------------------------------------------------ def frame(self, t: float, dt: float) -> Tuple[np.ndarray, np.ndarray]: ctl = self.controls(t) sh = self.shape if ctl.stage < 2: z = np.zeros((sh.H, sh.W), np.uint8) return z, z self._tick_bursts(t, dt, ctl) energy = 1.0 + 0.16 * self.burst flick = (0.90 + 0.05 * math.sin(TAU * 3.3 * t + 1.0) + 0.03 * math.sin(TAU * 7.9 * t) + 0.02 * math.sin(TAU * 13.1 * t + 2.0)) # solid layers: flame + lamp, revealed from the centre outwards flame = self.fire.field(t, ctl.flame_grow, energy) shimmer = 0.84 + 0.16 * np.sin(t * sh.rate + sh.phase) light = float(np.clip(flick + 0.10 * self.burst, 0.8, 1.15)) bowl = sh.bowl_heat * light * shimmer rev = np.clip((ctl.field_gain * 1.35 - sh.rv) / 0.22, 0.0, 1.0) field = np.maximum(flame, bowl) * rev field = field * (0.92 + 0.08 * np.sin(t * sh.rate * 1.3 + sh.phase * 2.0)) # ambient glow: a dithered, twinkling halo g = sh.glow * ctl.glow_gain * self.cfg.glow * (0.82 + 0.18 * flick + 0.25 * self.burst) tw = 0.5 + 0.5 * np.sin(t * sh.rate2 + sh.phase) glow_h = np.where(sh.r2 < np.clip(g * 1.25, 0.0, 1.0), g * 0.40 * (0.40 + 0.75 * tw), 0.0) field = np.maximum(field, glow_h) # digital accents: a few cyan/blue cells twinkling in the glow acc_f = (sh.r3 > 1.0 - 0.020 * self.cfg.accent) & (g > 0.10) & (tw > 0.80) & (field < 0.30) field = np.where(acc_f, 0.30 + 0.5 * tw, field) var_f = ((t * sh.rate + sh.phase * 2.0).astype(np.int64)) & 3 # particle layer on top hp, vp, ap = self.particles.render(t, dt, ctl) use_p = hp > field heat = np.where(use_p, hp, field) var = np.where(use_p, vp, var_f) acc = np.where(use_p, ap, acc_f) ch, col = shade(heat, var, acc) # outline glyphs that follow the curve of the lamp edge = sh.edge_sel & ~use_p & (rev > 0.5) & (ch != 0) ch = np.where(edge, sh.edge_glyph, ch).astype(np.uint8) return ch, col # JUPYTER NOTEBOOK RENDERER from IPython.display import display, HTML, clear_output from html import escape def frame_to_html(ch, col): rows = [] for rr in range(ch.shape[0]): pieces, run, last_color = [], [], None for cc in range(ch.shape[1]): gid = int(ch[rr, cc]) if gid == 0: char, color = " ", None else: char = GLYPHS[gid] rgb = COLOR_TABLE[int(col[rr, cc])] color = "#{:02x}{:02x}{:02x}".format(*map(int, rgb)) if color != last_color and run: txt = escape("".join(run)).replace(" ", "&nbsp;") pieces.append(txt if last_color is None else '<span style="color:{}">{}</span>'.format(last_color, txt)) run = [] run.append(char) last_color = color if run: txt = escape("".join(run)).replace(" ", "&nbsp;") pieces.append(txt if last_color is None else '<span style="color:{}">{}</span>'.format(last_color, txt)) rows.append("".join(pieces)) return "<br>".join(rows) def run_notebook(duration=25, fps=12, width=84, height=30, speed=1.0, density=0.85, glow=1.0, accent=1.0, seed=None): cfg = Config(fps=fps, speed=speed, intro=1.0, density=density, glow=glow, accent=accent, cols=width, rows=height, color_mode="truecolor", seed=seed, duration=duration) controller = AnimationController(cfg, width, height) frame_dt = 1.0 / max(1, fps) start = last = time.perf_counter() next_frame = start try: while True: now = time.perf_counter() elapsed = now - start if duration > 0 and elapsed >= duration: break dt = min(now - last, 0.1) ch, col = controller.frame(elapsed * speed, dt * speed) artwork = frame_to_html(ch, col) clear_output(wait=True) html = ('<div style="background:radial-gradient(ellipse at 50% 48%, #241044 0%, #090817 48%, #020207 100%);padding:18px 12px;border:1px solid #49306f;border-radius:16px;text-align:center;box-shadow:0 0 28px rgba(130,70,255,.22),inset 0 0 28px rgba(0,220,220,.08);">' '<div style="background:linear-gradient(90deg,#ff5d8f,#ffd166,#5ce1e6,#b58cff);-webkit-background-clip:text;color:transparent;font:bold 23px Arial;margin-bottom:12px;">HAPPY DIWALI ✨</div>' '<div style="display:inline-block;text-align:left;color:white;line-height:1.05;' 'font:13px Consolas, Courier New, monospace;white-space:nowrap;">' + artwork + '</div>' '<div style="background:linear-gradient(90deg,#ff8fab,#ffd166,#6ee7d8);-webkit-background-clip:text;color:transparent;font:bold 14px Arial;margin-top:12px;">' 'May your life shine bright! | CLCODING</div></div>') display(HTML(html)) last = now next_frame += frame_dt delay = next_frame - time.perf_counter() if delay > 0: time.sleep(delay) else: next_frame = time.perf_counter() except KeyboardInterrupt: pass finally: clear_output(wait=True) display(HTML('<h3 style="color:#e6a700">Shubh Deepavali! — CLCODING</h3>')) # Run the animation. Adjust these settings as needed. run_notebook(duration=25, fps=12, width=84, height=30)

Popular Posts

Categories

100 Python Programs for Beginner (119) AI (355) Android (25) AngularJS (1) API (2) Assembly Language (2) aws (31) Azure (12) BI (10) book (1) Books (359) Bootcamp (20) C (78) C# (12) C++ (83) cloud (1) Course (96) Coursera (306) Cybersecurity (37) data (10) Data Analysis (48) Data Analytics (31) data management (16) Data Science (442) Data Strucures (21) Deep Learning (227) Django (17) Downloads (3) edx (21) Engineering (15) Euron (30) Events (9) Excel (24) Finance (13) flask (4) flutter (1) FPL (17) Gadgets (1) Generative AI (79) Git (14) Google (55) Hadoop (3) HTML Quiz (1) HTML&CSS (48) IBM (43) IoT (3) IS (25) Java (99) Leet Code (4) Machine Learning (416) Meta (24) MICHIGAN (5) microsoft (13) Nvidia (8) Pandas (16) PHP (20) Projects (36) Python (1403) Python Bug (1) Python Coding Challenge (1298) Python Library (29) Python Mathematics (22) Python Mistakes (51) Python Pattern Challenge (36) python quixz (1) Python Quiz (677) Python Tips (114) Questions (3) R (72) React (7) Scripting (3) security (5) Selenium Webdriver (4) Software (21) SQL (56) Udemy (24) UX Research (1) web application (11) Web development (9) web scraping (3)

Followers

Python Coding for Kids ( Free Demo for Everyone)