Rock Paper Scissors
Algorithm
Bot Development
Game Theory
Machine Learning

Rock paper Scissors bot algorithm

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

A Rock Paper Scissors bot can be trivial if it plays random every turn, but stronger bots exploit predictable opponent patterns. The core challenge is balancing exploitation of observed behavior with enough randomness to avoid becoming predictable yourself. A practical bot usually combines several lightweight strategies and adapts as match history grows.

Start with Correct Game Logic

Before strategy, ensure move evaluation is correct and testable.

python
1MOVES = ["rock", "paper", "scissors"]
2BEATS = {
3    "rock": "scissors",
4    "paper": "rock",
5    "scissors": "paper",
6}
7
8
9def winner(me: str, opp: str) -> int:
10    if me == opp:
11        return 0
12    return 1 if BEATS[me] == opp else -1

Return convention in this example:

  • 1 means bot wins,
  • 0 means draw,
  • -1 means bot loses.

Stable scoring logic prevents strategy evaluation bugs later.

Frequency-Based Predictor

A simple non-random strategy assumes opponent repeats certain moves more often.

python
1from collections import Counter
2
3COUNTER_MOVE = {
4    "rock": "paper",
5    "paper": "scissors",
6    "scissors": "rock",
7}
8
9
10def choose_by_frequency(opponent_history):
11    if not opponent_history:
12        return "rock"
13    common_move, _ = Counter(opponent_history).most_common(1)[0]
14    return COUNTER_MOVE[common_move]

This works well against naive humans and scripted bots with biased distributions.

Short-Context Pattern Predictor

Some opponents follow local patterns, such as alternating moves. A context model can exploit that.

python
1from collections import defaultdict, Counter
2
3
4def build_transition_counts(history):
5    counts = defaultdict(Counter)
6    for i in range(len(history) - 1):
7        prev_m = history[i]
8        next_m = history[i + 1]
9        counts[prev_m][next_m] += 1
10    return counts
11
12
13def choose_by_last_move(history):
14    if len(history) < 2:
15        return choose_by_frequency(history)
16
17    transitions = build_transition_counts(history)
18    last_move = history[-1]
19    if not transitions[last_move]:
20        return choose_by_frequency(history)
21
22    predicted = transitions[last_move].most_common(1)[0][0]
23    return COUNTER_MOVE[predicted]

This one-step Markov style method remains lightweight and often beats pure frequency when local patterns exist.

Mixed Strategy to Avoid Exploitability

A deterministic bot can be reverse engineered. Add controlled randomness.

python
1import random
2
3
4def choose_mixed(history, exploit_prob=0.75):
5    if random.random() < exploit_prob:
6        return choose_by_last_move(history)
7    return random.choice(MOVES)

This preserves exploitation while preventing opponents from fully predicting the bot.

You can tune exploit_prob based on match length. Early rounds can be more random, later rounds more exploitative once behavior signal is stronger.

Evaluate Bot Performance with Simulation

Always evaluate strategy against multiple opponent types.

python
1
2def fixed_opponent(move):
3    return move
4
5
6def play(bot_fn, rounds=200):
7    opp_history = []
8    score = 0
9    for _ in range(rounds):
10        opp = fixed_opponent("rock")
11        me = bot_fn(opp_history)
12        score += winner(me, opp)
13        opp_history.append(opp)
14    return score
15
16
17print(play(choose_mixed, rounds=200))

Extend evaluation to random opponents and scripted pattern opponents. A strong bot should outperform predictable opponents while not collapsing against random play.

Practical Improvements

Useful upgrades beyond baseline logic:

  • maintain recent-window statistics instead of full-history,
  • detect strategy drift and reset model state,
  • ensemble multiple predictors with weighted voting,
  • use confidence thresholds before exploiting predictions.

Avoid overfitting to one test opponent. Build a small benchmark suite with diverse behaviors.

Dynamic Strategy Switching by Confidence

A useful extension is confidence-aware switching. If predictor confidence is low, play closer to random. If confidence is high, exploit aggressively.

python
1from collections import Counter
2
3def choose_with_confidence(history, min_conf=0.55):
4    if len(history) < 5:
5        return choose_mixed(history, exploit_prob=0.4)
6
7    last = history[-1]
8    transitions = build_transition_counts(history)
9    total = sum(transitions[last].values()) if transitions[last] else 0
10    if total == 0:
11        return choose_mixed(history, exploit_prob=0.5)
12
13    predicted, count = transitions[last].most_common(1)[0]
14    confidence = count / total
15    if confidence < min_conf:
16        return choose_mixed(history, exploit_prob=0.35)
17    return COUNTER_MOVE[predicted]

This prevents overreacting to noise in short or inconsistent histories.

Common Pitfalls

  • Using deterministic logic without randomness, making bot exploitable.
  • Evaluating only against one opponent style and overestimating strength.
  • Letting stale history dominate when opponent behavior changes.
  • Mixing scoring and strategy code in one function, which hurts testability.
  • Ignoring draw rates when comparing strategy quality.

Summary

  • Build a correct, testable game core before strategy work.
  • Frequency and short-context predictors are strong lightweight baselines.
  • Add controlled randomness to reduce exploitability.
  • Evaluate against multiple opponent models, not a single script.
  • Treat bot design as adaptive decision-making, not one fixed rule.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

All Rights Reserved.