2026-04-21 15:46:30 -04:00
|
|
|
from django.db.models import Q
|
|
|
|
|
|
|
|
|
|
from apps.epic.models import Room, RoomInvite
|
|
|
|
|
|
|
|
|
|
|
2026-05-01 00:11:40 -04:00
|
|
|
# ── Game-wide constants ────────────────────────────────────────────────────
|
|
|
|
|
# Reversal probability applied to any card pulled from a stack, anywhere in
|
|
|
|
|
# the game (PICK SEA initially; future phases — gameplay draws etc. — will
|
|
|
|
|
# share this single source of truth). Stub for a future per-user profile
|
|
|
|
|
# override: callers MUST go through stack_reversal_probability(user, room)
|
|
|
|
|
# rather than referencing the constant directly so the user-config hookup is
|
|
|
|
|
# a one-line change inside the helper.
|
|
|
|
|
STACK_REVERSAL_PROBABILITY = 0.25
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def stack_reversal_probability(user=None, room=None):
|
|
|
|
|
"""Reversal probability for a draw stack in this user's context.
|
|
|
|
|
|
|
|
|
|
Current behavior: returns the module default for everyone. Plumbing point
|
|
|
|
|
for a forthcoming per-user setting — when that lands, swap the body to
|
|
|
|
|
something like `return getattr(user.profile, 'reversal_rate', STACK_REVERSAL_PROBABILITY)`
|
|
|
|
|
and every call site picks up the per-user value automatically.
|
|
|
|
|
"""
|
|
|
|
|
return STACK_REVERSAL_PROBABILITY
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-21 15:46:30 -04:00
|
|
|
def _planet_house(degree, cusps):
|
|
|
|
|
"""Return 1-based house number for a planet at ecliptic degree.
|
|
|
|
|
|
|
|
|
|
cusps is the 12-element list from PySwiss where cusps[i] is the start of
|
|
|
|
|
house i+1. Handles the wrap-around case where a cusp crosses 0°/360°.
|
|
|
|
|
"""
|
|
|
|
|
degree = degree % 360
|
|
|
|
|
for i in range(12):
|
|
|
|
|
start = cusps[i] % 360
|
|
|
|
|
end = cusps[(i + 1) % 12] % 360
|
|
|
|
|
if start < end:
|
|
|
|
|
if start <= degree < end:
|
|
|
|
|
return i + 1
|
|
|
|
|
else: # wrap-around: e.g. cusp at 350° → next at 10°
|
|
|
|
|
if degree >= start or degree < end:
|
|
|
|
|
return i + 1
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _compute_distinctions(planets, houses):
|
|
|
|
|
"""Return dict {house_number_str: planet_count} for all 12 houses."""
|
|
|
|
|
cusps = houses['cusps']
|
|
|
|
|
counts = {str(i): 0 for i in range(1, 13)}
|
|
|
|
|
for planet_data in planets.values():
|
|
|
|
|
h = _planet_house(planet_data['degree'], cusps)
|
|
|
|
|
counts[str(h)] += 1
|
|
|
|
|
return counts
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def rooms_for_user(user):
|
|
|
|
|
"""Return a queryset of rooms the user owns, has a gate slot in, or is invited to."""
|
|
|
|
|
return Room.objects.filter(
|
|
|
|
|
Q(owner=user) |
|
|
|
|
|
Q(gate_slots__gamer=user) |
|
|
|
|
|
Q(invites__invitee_email=user.email, invites__status=RoomInvite.PENDING)
|
|
|
|
|
).distinct()
|