rollcoin.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. from dataclasses import dataclass, field
  2. from typing import Optional
  3. from rollbot import as_command, initialize_data, RollbotFailure
  4. from rollbot.injection import Data, Sender, Config, Const, Arg
  5. # View
  6. # !wallet - shows your number of rollcoins, NFTs (Non-Functional Tamagotchis), and market balance
  7. # !blockchain - shows the contents of all wallets and the rollcoins in the treasury
  8. # Generate
  9. # !mine - provides a sudoku that can be solved for rollcoins, which also adds to the treasury
  10. # !appraise - rollbot will purchase a post, based indirectly on number of likes received
  11. # Spend
  12. # !tip - transfer rollcoins from one person to another
  13. # !gacha - insert a rollcoin (which enters the treasury), receive a random NFT
  14. # !donate - split an amount and donate it to everyone
  15. # !bet - put some amount of rollcoins into the market, some portion of which will be put in the treasury, evolves market
  16. # !cash - take some amount of rollcoins from the market, up to investment + the number of coins in the treasury, evolves market
  17. # Admin
  18. # !deflate - deflate all coins by a power of 10
  19. # !brrr - print some number of coins into the treasury
  20. # !mine !clear - clear the current mining puzzle
  21. # !market !force - force the market to transition to a given state
  22. @initialize_data
  23. @dataclass
  24. class RollcoinState:
  25. treasury: float
  26. mining_puzzle: Optional[list[list[int]]]
  27. market_state: int
  28. @initialize_data
  29. @dataclass
  30. class RollcoinWallet:
  31. balance: float = 1
  32. invested: float = 0
  33. investment_value: float = 0
  34. nfts: list[str] = field(default_factory=list)
  35. def __str__(self):
  36. s = f"Wallet: {self.balance} RollCoins\n"
  37. if self.invested > 0 or self.investment_value > 0:
  38. s += f"Invested: {self.investment_value} RollCoins (cost basis {self.invested})\n"
  39. if len(self.nfts) > 0:
  40. s += f"NFTs:\n"
  41. for nft in self.nfts:
  42. s += f"\t{nft}\n"
  43. return s.strip()
  44. SPECIAL_AMOUNTS = {
  45. "all": lambda limit: limit,
  46. "half": lambda limit: limit / 2,
  47. "frac": lambda limit: limit - int(limit),
  48. }
  49. def convert_amount(amount):
  50. if (conv := SPECIAL_AMOUNTS.get(amount.lower(), None)) is not None:
  51. return conv
  52. return float(amount)
  53. # injection values
  54. State = Data(RollcoinState).For(Const("ROLLCOIN_GLOBAL_STATE"), treasury=100, mining_puzzle=None, market_state=0)
  55. SenderWallet = Data(RollcoinWallet).For(Sender)
  56. WalletLookup = Config("rollcoin.wallet_names")
  57. @as_command
  58. def wallet(sender_wallet: SenderWallet):
  59. return f"You currently own...\n{sender_wallet}"
  60. @as_command
  61. async def blockchain(wallets: Data(RollcoinWallet), wallet_lookup: WalletLookup, state: State):
  62. response = f"Blockchain:\n\tTreasury contains {state.treasury} RollCoins\n\n"
  63. names = {v: k.title() for k, v in wallet_lookup.items()}
  64. async for (sender_id, wallet) in wallets.all():
  65. response += names.get(sender_id, f"Unnamed wallet {sender_id}") + ":\n"
  66. response += "\n".join("\t" + s for s in str(wallet).split("\n")) + "\n\n"
  67. return response.strip()
  68. @as_command
  69. async def tip(
  70. wallets: Data(RollcoinWallet),
  71. sender_id: Sender,
  72. sender_wallet: SenderWallet,
  73. wallet_lookup: WalletLookup,
  74. target_name: Arg(0, missing_msg="You must tell me who to tip!"),
  75. amount: Arg(1, convert=convert_amount, missing_msg="You must provide an amount to tip!", fail_msg="Could not parse {} as value"),
  76. ):
  77. if not isinstance(amount, float):
  78. # handle special converters
  79. amount = amount(sender_wallet.balance)
  80. if amount < 0:
  81. RollbotFailure.INVALID_ARGUMENTS.raise_exc(f"Amount must be positive, not {amount}")
  82. if amount == 0:
  83. return "Sorry! You don't have any rollcoins right now - try mining!"
  84. if amount > sender_wallet.balance:
  85. return f"Sorry! You only have {sender_wallet.balance} RollCoins available - try mining for more!"
  86. if (target_id := wallet_lookup.get(target_name.lower(), None)) is None:
  87. RollbotFailure.INVALID_ARGUMENTS.raise_exc(f"Cannot find wallet belonging to {target_name}")
  88. target_wallet = await wallets.load_or(target_id)
  89. sender_wallet.balance -= amount
  90. target_wallet.balance += amount
  91. await wallets.save(target_id, target_wallet)
  92. await wallets.save(sender_id, sender_wallet)
  93. return f"Done! {target_name} now has {target_wallet.balance} RollCoins!"