A simple, robust, and well-documented terminal game (like Rock–Paper–Scissors) implemented in Python.
This README explains everything: what the game is, how to run it, how the logic works, examples, automated tests, CI, and next steps — so anyone can understand and contribute by reading this file alone.
- Overview
- Features
- Quick start
- Usage & options
- Examples (commands & sample output)
- How the game logic works (concise explanation)
- Design & code structure — what to look for in the source
- Testing (pytest)
- Continuous Integration (GitHub Actions)
- Troubleshooting & FAQ
- Contributing
- Future improvements
- License
Snake — Water — Gun is a small, beginner-friendly terminal game. Players choose among Snake, Water, or Gun. The computer picks randomly.
Outcome rules:
- Snake drinks Water → Snake beats Water
- Water drowns Gun → Water beats Gun
- Gun shoots Snake → Gun beats Snake
This repository contains a polished, testable Python implementation with clear separation of logic, user input handling, and a small test suite.
- Clean, modular Python code (functions for parsing, game logic, and main loop).
- Robust input handling (accepts s, w, g or full words snake, water, gun, case-insensitive).
- Two play modes: interactive (until Ctrl+C) and fixed rounds (-n N).
- Unit-testable logic (
determine_outcome) so you can add automated tests. - Ready for CI (workflow included).
- Designed to be extended (AI opponent strategies, best-of-N, GUI).
-
Clone the repository (or download and extract the ZIP).
-
Make sure you have Python 3.9+ installed.
-
(Optional) Create a virtual environment:
python -m venv .venv source .venv/bin/activate # macOS / Linux .\.venv\Scripts\activate # Windows PowerShell
-
Run the game:
python Snake-Water-Gun_Game.py
If you rename the script, update the command accordingly. The current filename is Snake-Water-Gun_Game.py.
Run without options. The game keeps asking for choices until you press Ctrl+C:
python Snake-Water-Gun_Game.pyValid inputs (case-insensitive):
- s or snake → Snake
- w or water → Water
- g or gun → Gun
Invalid inputs are ignored (not counted as rounds) and the program prompts again.
Play a fixed number of valid rounds (invalid input does not count):
python Snake-Water-Gun_Game.py -n 5This runs until 5 valid rounds are played and then prints final stats.
Not included by default, but recommended: provide --best-of 5 to end when a player reaches majority wins (first to 3 wins in best-of-5).
Pseudo-logic:
- target_wins = best_of // 2 + 1
- keep playing rounds until either player reaches target_wins
- invalid inputs still do not count
This is intentionally straightforward to add in main().
$ python Snake-Water-Gun_Game.py
Welcome to Snake - Water - Gun!
Play until you press Ctrl-C (invalid inputs will be ignored).
Choose: S (Snake), W (Water), G (Gun). You can also type full words.
1. Enter your choice (S/W/G): s
---> You chose: Snake | Computer chose: Water
..:: You Win!
2. Enter your choice (S/W/G): g
---> You chose: Gun | Computer chose: Snake
..:: You Win!
^C
Game interrupted by user.
==> Score so far (after 2 rounds): Wins: 2, Losses: 0, Draws: 0
$ python Snake-Water-Gun_Game.py -n 3
We'll play 3 valid rounds. Invalid inputs are not counted.
1. Enter your choice (S/W/G): w
---> You chose: Water | Computer chose: Gun
..:: You Win!
2. Enter your choice (S/W/G): s
---> You chose: Snake | Computer chose: Gun
..:: You Lose!
3. Enter your choice (S/W/G): g
---> You chose: Gun | Computer chose: Gun
..:: Match Draw.
==> Final result after 3 rounds: Wins: 1, Losses: 1, Draws: 1
There are three choices. A compact and reliable way to check wins uses modular arithmetic.
Mapping used (in the provided code):
- 0 → Snake
- 1 → Water
- 2 → Gun
Win rule (single condition):
- If player == computer → draw
- Else, player wins if (player - computer) % 3 == 2
- Otherwise player loses
Why this works:
- (0 - 1) % 3 = 2 → Snake (0) beats Water (1)
- (1 - 2) % 3 = 2 → Water (1) beats Gun (2)
- (2 - 0) % 3 = 2 → Gun (2) beats Snake (0)
Using modular arithmetic keeps the logic compact and avoids long chains of if statements.
Open the main script at Snake-Water-Gun_Game.py. Key parts:
parse_args()— parses -n/--rounds.determine_outcome(player, computer)— pure function, returns "win" | "lose" | "draw". Easy to test.play_round(raw_input)— normalizes input, validates, chooses computer move, returns outcome and choices. Raises ValueError for invalid inputs.main()— orchestration loop, score tracking, friendly messages and graceful handling of KeyboardInterrupt.
This separation makes each part simple to reason about and unit-testable.
A small test file exists at tests/test_logic.py. It validates all win/lose/draw combinations for determine_outcome.
Run tests locally:
python -m pip install -r requirements.txt
pytest -qIf you add new logic, please add tests for it.
The workflow is defined in .github/workflows/python-ci.yml. It runs on push and pull requests for main/master and includes:
- A Python version matrix (3.9–3.12)
- Dependency install (if requirements.txt exists)
- Pytest test run
- A non-blocking smoke run of the game using -n 1 and a single input
This setup keeps CI green and ensures the project stays healthy.
Q: The CI job hangs — why?
If a script waits for interactive input, CI can hang. The workflow avoids this by running the game with -n 1 and providing input via stdin. Tests only exercise pure logic.
Q: My input isn't recognized.
Valid inputs are s/snake, w/water, g/gun (case-insensitive). Leading/trailing whitespace is ignored. Anything else is rejected and you are prompted again.
Q: Can I change the mapping back to 1, 0, -1?
You can, but the modular arithmetic rule uses the index mapping 0, 1, 2. The current approach is recommended for clarity and extensibility.
Fork the repo.
Create a feature branch:
git checkout -b feature/my-new-featureAdd tests for new behavior.
Open a pull request describing your changes.
Please follow these guidelines:
- Keep functions small and single-responsibility.
- Add or update tests for any logic changes.
- Keep the CLI friendly and avoid blocking behavior for automated runs.
- Add --best-of N mode (first to majority wins).
- Add an adaptive AI opponent that learns player tendencies.
- Add a small web UI with Streamlit or Flask.
- Track historical stats (save to JSON) and show leaderboards.
- Add code coverage reporting to CI and a coverage badge in README.
This project is released under the MIT License. See LICENSE for details.