""" world_zodiac.py — the Chinese zodiac + the turning seasons. Computed, no API. The year's animal and element (a 12- and 60-year wheel), plus the "hour animal" that turns every two hours — a slow calendar sequencer the day walks through. And the seasons: northern vs southern summer intensity from the day of the year, so "all data that happens in summer" has a signal to gate against, both hemispheres. Emits: /gs/zodiac/year the year's animal (0..1 over Rat..Pig) /gs/zodiac/element the year's element (0..1 over Wood/Fire/Earth/Metal/Water) /gs/zodiac/hour the current 2-hour animal (0..1) — a 12-step day cycle /gs/season/north northern-hemisphere summer intensity (0..1) /gs/season/south southern-hemisphere summer intensity (0..1) """ from __future__ import annotations import argparse import datetime import math import time from pythonosc.udp_client import SimpleUDPClient ANIMALS = ["Rat", "Ox", "Tiger", "Rabbit", "Dragon", "Snake", "Horse", "Goat", "Monkey", "Rooster", "Dog", "Pig"] ELEMENTS = ["Wood", "Fire", "Earth", "Metal", "Water"] def main(): ap = argparse.ArgumentParser() ap.add_argument("--host", default="127.0.0.1") ap.add_argument("--port", type=int, default=9000) ap.add_argument("--interval", type=float, default=30.0) args = ap.parse_args() client = SimpleUDPClient(args.host, args.port) print("[zodiac] emitting /gs/zodiac/* and /gs/season/* (computed, no API)") last_hour = None while True: try: utc = datetime.datetime.now(datetime.timezone.utc) y = utc.year year_animal = (y - 4) % 12 year_element = ((y - 4) % 10) // 2 h = datetime.datetime.now().hour # local hour hour_animal = ((h + 1) // 2) % 12 doy = utc.timetuple().tm_yday north = 0.5 + 0.5 * math.cos(2 * math.pi * (doy - 172) / 365.25) south = 1.0 - north client.send_message("/gs/zodiac/year", year_animal / 11.0) client.send_message("/gs/zodiac/element", year_element / 4.0) client.send_message("/gs/zodiac/hour", hour_animal / 11.0) client.send_message("/gs/season/north", north) client.send_message("/gs/season/south", south) if hour_animal != last_hour: print(f" ☯ year of the {ELEMENTS[year_element]} " f"{ANIMALS[year_animal]} · hour of the {ANIMALS[hour_animal]}") last_hour = hour_animal except Exception as e: # noqa print(f"[zodiac] warn: {e}") time.sleep(args.interval) if __name__ == "__main__": main()