Create rock_paper_scissors.py#1534
Conversation
Rock Paper Scissors mini game built on genlayer's intelligent contract
📝 WalkthroughWalkthroughA new Rock Paper Scissors smart contract class is introduced, featuring game state tracking (player wins, AI wins, ties), a play method that validates moves, generates AI moves nondeterministically, computes game outcomes, and updates counters accordingly. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment Tip Flake8 can be used to improve the quality of Python code reviews.Flake8 is a Python linter that wraps PyFlakes, pycodestyle and Ned Batchelder's McCabe script. To configure Flake8, add a '.flake8' or 'setup.cfg' file to your project root. See Flake8 Documentation for more details. |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
examples/contracts/rock_paper_scissors.py (1)
11-11: Add return annotations to the remaining untyped helpers.
__init__and the nestednondethelper are the only callables in this file without return annotations, so the example still misses the repo's type-hint requirement.✍️ Proposed fix
- def __init__(self): + def __init__(self) -> None: self.last_result = "No game played yet" self.player_wins = 0 self.ai_wins = 0 self.ties = 0 @@ - def nondet(): + def nondet() -> str: res = gl.nondet.exec_prompt(prompt) backticks = "``" + "`" res = res.replace(backticks + "json", "").replace(backticks, "")As per coding guidelines, "Include type hints in all Python code".
Also applies to: 37-37
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/contracts/rock_paper_scissors.py` at line 11, Add explicit return type annotations to the untyped callables: annotate the class initializer def __init__(self) with -> None and annotate the nested helper nondet(...) with its appropriate return type (e.g., -> int or -> bool depending on its implementation) so both signatures include type hints; update the signatures in the RockPaperScissors class (the __init__ method) and the nested nondet function to satisfy the repository's "Include type hints in all Python code" rule.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@examples/contracts/rock_paper_scissors.py`:
- Around line 19-21: The code currently silently returns when player_move is
invalid (the player_move.lower().strip() check) which leaves last_result
unchanged; instead, handle invalid moves explicitly by either raising an error
or persisting an explicit invalid outcome: update the same state path that
records the game result (last_result) with a clear status like "invalid move"
and any metadata (original input) or raise a ValueError so callers see the
failure; locate the invalid-check around player_move and modify the branch to
set last_result (or throw) rather than returning silently.
- Around line 37-47: The nondet() helper currently parses JSON and directly
accesses dat["move"], so any parse/key/type errors happen before
gl.eq_principle.strict_eq(nondet) runs; move and implement normalization and
fallback inside nondet(): catch json.loads and KeyError/TypeError, strip and
lower the extracted value (use dat.get("move") safely), verify it's one of
"rock","paper","scissors", and on any error or invalid value return the default
"rock" so strict_eq always receives a deterministic string; keep the existing
backtick-cleaning but ensure all validation and defaulting happens before
returning from nondet().
- Line 2: Replace the wildcard import in
examples/contracts/rock_paper_scissors.py (from genlayer import *) with explicit
imports of only the symbols this example uses: inspect the file to determine
which functions/classes/constants from genlayer are referenced, then change the
import to "from genlayer import <SymbolA>, <SymbolB>, ..." (and update other
example files the same way for consistency) so that F403/F405 lint errors are
avoided without changing global ruff config; if you truly intend to exempt
examples from linting instead, update the repo CI/pre-commit configuration to
exclude the examples directory and document that decision.
---
Nitpick comments:
In `@examples/contracts/rock_paper_scissors.py`:
- Line 11: Add explicit return type annotations to the untyped callables:
annotate the class initializer def __init__(self) with -> None and annotate the
nested helper nondet(...) with its appropriate return type (e.g., -> int or ->
bool depending on its implementation) so both signatures include type hints;
update the signatures in the RockPaperScissors class (the __init__ method) and
the nested nondet function to satisfy the repository's "Include type hints in
all Python code" rule.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 801b7df4-c197-4439-bc80-5f0eb54719ce
📒 Files selected for processing (1)
examples/contracts/rock_paper_scissors.py
| @@ -0,0 +1,78 @@ | |||
| # { "Depends": "py-genlayer:1jb45aa8ynh2a9c9xn3b7qqh8sm5q93hwfp7jqmwsfhh8jpz09h6" } | |||
| from genlayer import * | |||
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Ruff configuration ==="
configs="$(fd -HI '^(pyproject\.toml|ruff\.toml|\.ruff\.toml|setup\.cfg)$' . || true)"
if [ -n "$configs" ]; then
while IFS= read -r file; do
[ -n "$file" ] || continue
echo "-- $file"
rg -n -C2 'F403|F405|per-file-ignores|extend-ignore|ignore' "$file" || true
done <<< "$configs"
else
echo "No Ruff config files found."
fi
echo
echo "=== GenLayer import patterns in the repo ==="
rg -n -C2 'from genlayer import \*|import genlayer' . || trueRepository: genlayerlabs/genlayer-studio
Length of output: 12946
Clarify whether from genlayer import * is intentional or if examples should be exempt from linting.
This star import pattern is used consistently across the entire codebase (tests, examples, and integration contracts), but F403 and F405 are not listed in the global ignore rules in ruff.toml. Either confirm that examples are excluded from linting in pre-commit or CI configuration, or adopt explicit imports if the SDK API is intended to be visible at the call site.
🧰 Tools
🪛 Ruff (0.15.5)
[error] 2-2: from genlayer import * used; unable to detect undefined names
(F403)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/contracts/rock_paper_scissors.py` at line 2, Replace the wildcard
import in examples/contracts/rock_paper_scissors.py (from genlayer import *)
with explicit imports of only the symbols this example uses: inspect the file to
determine which functions/classes/constants from genlayer are referenced, then
change the import to "from genlayer import <SymbolA>, <SymbolB>, ..." (and
update other example files the same way for consistency) so that F403/F405 lint
errors are avoided without changing global ruff config; if you truly intend to
exempt examples from linting instead, update the repo CI/pre-commit
configuration to exclude the examples directory and document that decision.
| player_move = player_move.lower().strip() | ||
| if player_move not in ["rock", "paper", "scissors"]: | ||
| return |
There was a problem hiding this comment.
Don't silently accept invalid moves.
Line 21 returns without updating last_result or surfacing an error, so a caller can get a successful write and then read the previous game's result back. Revert here, or persist an explicit invalid-move outcome instead.
🛠️ Proposed fix
player_move = player_move.lower().strip()
if player_move not in ["rock", "paper", "scissors"]:
- return
+ raise ValueError("player_move must be one of: rock, paper, scissors")📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| player_move = player_move.lower().strip() | |
| if player_move not in ["rock", "paper", "scissors"]: | |
| return | |
| player_move = player_move.lower().strip() | |
| if player_move not in ["rock", "paper", "scissors"]: | |
| raise ValueError("player_move must be one of: rock, paper, scissors") |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/contracts/rock_paper_scissors.py` around lines 19 - 21, The code
currently silently returns when player_move is invalid (the
player_move.lower().strip() check) which leaves last_result unchanged; instead,
handle invalid moves explicitly by either raising an error or persisting an
explicit invalid outcome: update the same state path that records the game
result (last_result) with a clear status like "invalid move" and any metadata
(original input) or raise a ValueError so callers see the failure; locate the
invalid-check around player_move and modify the branch to set last_result (or
throw) rather than returning silently.
| def nondet(): | ||
| res = gl.nondet.exec_prompt(prompt) | ||
| backticks = "``" + "`" | ||
| res = res.replace(backticks + "json", "").replace(backticks, "") | ||
| dat = json.loads(res) | ||
| return dat["move"].lower().strip() | ||
|
|
||
| ai_move = gl.eq_principle.strict_eq(nondet) | ||
|
|
||
| if ai_move not in ["rock", "paper", "scissors"]: | ||
| ai_move = "rock" |
There was a problem hiding this comment.
Move AI-response fallback inside nondet().
The fallback on Lines 46-47 only runs after gl.eq_principle.strict_eq(nondet) has already succeeded. If the model returns malformed JSON, omits "move", or produces a non-string, json.loads/dat["move"] will raise before you ever reach that branch, and divergent invalid outputs will still fail consensus. Normalize and default inside nondet() so strict_eq sees a deterministic value.
🛠️ Proposed fix
- def nondet() -> str:
+ def nondet() -> str:
res = gl.nondet.exec_prompt(prompt)
backticks = "``" + "`"
- res = res.replace(backticks + "json", "").replace(backticks, "")
- dat = json.loads(res)
- return dat["move"].lower().strip()
+ cleaned = res.replace(backticks + "json", "").replace(backticks, "")
+ try:
+ move = json.loads(cleaned)["move"].lower().strip()
+ except (TypeError, KeyError, json.JSONDecodeError):
+ return "rock"
+ return move if move in ["rock", "paper", "scissors"] else "rock"
ai_move = gl.eq_principle.strict_eq(nondet)
-
- if ai_move not in ["rock", "paper", "scissors"]:
- ai_move = "rock"🧰 Tools
🪛 Ruff (0.15.5)
[error] 38-38: gl may be undefined, or defined from star imports
(F405)
[error] 44-44: gl may be undefined, or defined from star imports
(F405)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/contracts/rock_paper_scissors.py` around lines 37 - 47, The nondet()
helper currently parses JSON and directly accesses dat["move"], so any
parse/key/type errors happen before gl.eq_principle.strict_eq(nondet) runs; move
and implement normalization and fallback inside nondet(): catch json.loads and
KeyError/TypeError, strip and lower the extracted value (use dat.get("move")
safely), verify it's one of "rock","paper","scissors", and on any error or
invalid value return the default "rock" so strict_eq always receives a
deterministic string; keep the existing backtick-cleaning but ensure all
validation and defaulting happens before returning from nondet().
|
Sorry, not relevant |
Rock Paper Scissors mini game built on GenLayer's Intelligent Contract
What
Why
Testing done
Decisions made
Checks
User facing release notes
New example contract: Rock Paper Scissors game where a player competes against an AI validator on-chain.
Summary by CodeRabbit
Release Notes