The RPSA Guide
Everything you need to go from a blank file to a strategy that competes for credits. Read this once and you'll know more than most new contestants.
The game
Rock beats scissors. Scissors beats paper. Paper beats rock. You play 2,000 rounds against another bot. Your score isn't your win rate — it's your credit balance when the match ends.
Every round you win earns a base credit. Every round you lose costs one. But credits are also affected by perks — special abilities bought at mid-match auctions that can amplify your gains, protect against losses, or disrupt your opponent. A strategy that wins 55% of rounds but ignores the economy can still lose to a strategy that wins 48% of rounds but plays the auction well.
Your first bot
A strategy is a Python class. The minimum you need is one method:
from rpsa_sdk import Strategy
class MyFirstBot(Strategy):
def next_move(self) -> str:
return "rock" # play rock every timeUpload it, run it against the demo bots, and watch what happens. The goal at this stage is to get something deployed — you can improve it from there.
Tracking your opponent
Most improvements come from one insight: opponents have patterns. If someone plays rock 60% of the time, you should play paper 100% of the time. The SDK provides helpers to track this:
from rpsa_sdk import Strategy
from rpsa_sdk.helpers import MoveHistory, counter
class CounterMostCommon(Strategy):
def __init__(self):
self.history = MoveHistory() # tracks last N opponent moves
def notify(self, own_move: str, opp_move: str) -> None:
self.history.record(opp_move) # called after every round
def next_move(self) -> str:
return counter(self.history.most_common()) # beats their most frequent moveMoveHistory keeps a sliding window of recorded moves and provides most_common(), counts(), and last(n). counter(move) returns the move that beats the given move. This pattern — record in notify(), decide in next_move() — is the foundation of almost every competitive strategy.
Why capital matters
The economy runs on credits. You start each match with a base balance. You earn or lose credits each round based on the outcome. But mid-match, an auction opens: perks go to the highest bidder, and those perks can change the economics of the remaining rounds.
A perk like PowerPlay multiplies your credit gain for a window of rounds. If you win the auction and then win four rounds straight, the payoff can be 3× what you'd have earned unperked. This means a strategy that loses more rounds but holds reserves for the right auction can finish higher than a strategy with a better win rate. To participate, set uses_context = True and use the injected Context to check the market and place bids. See the SDK docs for the full API.
Testing locally
Before uploading, test your strategy against the built-in bots using the offline harness:
from rpsa_sdk.harness import play
from rpsa_sdk.examples.always_rock import AlwaysRock
transcript = play(MyFirstBot(), AlwaysRock(), rounds=100)
print(transcript.wins_a, transcript.draws, transcript.wins_b)
# → 0 0 100 (rock loses to... nothing, draws with rock)The harness uses the same RPS resolution as the live engine and calls notify() after every round so history-tracking strategies work correctly. Pass a seed for reproducible results:
t1 = play(MyBot(), RandomBot(), rounds=500, seed=42)
t2 = play(MyBot(), RandomBot(), rounds=500, seed=42)
assert t1.wins_a == t2.wins_a # identicalYou can also run the local validator to catch issues before upload:
python -m rpsa_sdk.validate my_strategy.pyThis runs the same three-stage check the server uses: static security scan, 100-round smoke test, and context test (if uses_context = True).
Uploading and iterating
When you're ready, upload your strategy file from the upload page. The platform validates it automatically (usually under 2 minutes) and shows the result on the strategy detail page.
After validation passes, enroll the strategy in the current season and activate it as your official entry. Then use the demo arena to run exhibition matches against the built-in bots at any time — no season required.