memoscan
← all memos

Memo 0xe24bc9d2…7609fd on Ethereum

L# { "Depends": "py-genlayer:test" } from genlayer import * # Score stored as: [player, level, moves, time_sec] # key = "address_level" e.g. "0xABC_3" class BlockholeLeaderboard(gl.Contract): """ Blockhole x GenLayer — On-chain leaderboard with AI-validated scores. Optimistic Democracy: 5 LLM validators vote on whether each score is humanly plausible before it's accepted on-chain. """ scores: TreeMap[str, str] # key="addr_level" value="moves,time_sec" week_number: u32 total_submissions: u32 def __init__(self): self.week_number = 1 self.total_submissions = 0 # ─── Write: submit a score ──────────────────────────────────────────────── def submit_score( self, player: str, level: u32, moves: u32, time_sec: u32, ) -> None: """ Submit a level completion score. GenLayer validators (each using a different LLM) vote on whether the score is humanly plausible — this IS the Optimistic Democracy showcase. """ def validate_score() -> bool: result = gl.exec_prompt( f"A player completed level {level} of a Bloxorz-style puzzle game " f"(roll a rectangular block into a hole) in {moves} moves and " f"{time_sec} seconds. " f"Level 1 is easy (~10 moves), level 10 is expert (~60+ moves). " f"Is this score plausible for a human player? " f"Answer only 'yes' or 'no'." ) return "yes" in result.lower() is_valid = gl.eq_principle_strict_eq(validate_score) if not is_valid: raise Exception("Score rejected by AI validators — not humanly plausible") key = f"{player}_{level}" existing = self.scores.get(key) # Keep personal best (lowest moves, tie-break by time) if existing is not None: parts = existing.split(",") best_moves = int(parts[0]) best_time = int(parts[1]) if moves > best_moves or (moves == best_moves and time_sec >= best_time): return # Not a new personal best self.scores[key] = f"{moves},{time_sec}" self.total_submissions += 1 # ─── Read: leaderboard queries ─────────────────────────────────────────── def get_leaderboard(self, level: u32) -> list: """Return top 10 scores for a specific level, sorted by moves then time.""" level_scores = [] suffix = f"_{level}" for key, val in self.scores.items(): if key.endswith(suffix): player = key[: -len(suffix)] parts = val.split(",") level_scores.append({ "player": player, "level": int(level), "moves": int(parts[0]), "time_sec": int(parts[1]), }) level_scores.sort(key=lambda s: (s["moves"], s["time_sec"])) return level_scores[:10] def get_global_leaderboard(self) -> list: """Return top players by total moves across all levels (fewer = better).""" player_totals: dict = {} for key, val in self.scores.items(): # key format: "0xADDR_LEVEL" last_underscore = key.rfind("_") if last_underscore == -1: continue player = key[:last_underscore] parts = val.split(",") if player not in player_totals: player_totals[player] = {"player": player, "total_moves": 0, "levels_done": 0} player_totals[player]["total_moves"] += int(parts[0]) player_totals[player]["levels_done"] += 1 ranked = sorted( player_totals.values(), key=lambda x: (-(x["levels_done"]), x["total_moves"]), ) return ranked[:10] def get_player_scores(self, player: str) -> list: """Return all scores for a given player address.""" result = [] prefix = f"{player}_" for key, val in self.scores.items(): if key.startswith(prefix): level = int(key[len(prefix):]) parts = val.split(",") result.append({ "level": level, "moves": int(parts[0]), "time_sec": int(parts[1]), }) return sorted(result, key=lambda s: s["level"]) def get_week(self) -> u32: return self.week_number def get_total_submissions(self) -> u32: return self.total_submissions # ─── Admin: weekly reset ───────────────────────────────────────────────── def weekly_reset(self) -> None: """Reset leaderboard for a new week.""" self.scores = gl.storage.inmem_allocate(TreeMap[str, str]) self.week_number += 1