Change the layout to fit the code on pyscript, now also with icons.

This commit is contained in:
Mr Finchum 2025-03-17 13:55:46 +01:00
parent 54cd9d838b
commit 25bc363a24
11 changed files with 170 additions and 170 deletions

View file

@ -1,9 +1,14 @@
# Rock Paper Scissors Card Game
Play [now](https://codebymrfinchum.pyscriptapps.com/rock-paper-scissors-card-game/latest/).
Play [now](https://codebymrfinchum.pyscriptapps.com/rock-paper-scissors-card-game/) on pyscriptapps.com or see info on [Gitlab](https://gitlab.com/CodeByMrFinchum/rock-paper-scissor-card-game).
A little game to play the classic Rock Paper Scissors game with a twist.
Instead of freely selecting between Rock, Paper, and Scissors, you get a hand of three cards, potentially with only rocks.
It is a semi-random 52-card deck with an unbalanced distribution of cards, meaning some cards are less likely to occur. For example, only 5 out of 52 cards are stones, so it is very unlikely the other player has a stone. Selecting scissors gives a higher chance of winning.
At the moment, there are two versions: a Python version for local usage (terminal), `local.py`, and a PyScript version, `main.py`.
The PyScript version can actually be accessed and played [online](https://codebymrfinchum.pyscriptapps.com/rock-paper-scissors-card-game/) on my [PyScript profile](https://pyscript.com/@codebymrfinchum).
It is built with PyScript and developed on [PyScript.com](). It is available on my [PyScript profile](https://pyscript.com/@codebymrfinchum) and [Gitlab repo](https://gitlab.com/CodeByMrFinchum/rock-paper-scissor-card-game).
You can also start the program local, download the repository go into the folder and do 'python3 -m http.server' and then open 'http://localhost:8000/' in your browser.
## Acknowledgment
- Help from ChatGPT
- Icons are from Flaticon, which are free to use for personal purposes. I also provided attribution in the HTML for the used images.

BIN
images/card-back.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
images/paper.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
images/rock.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
images/scissors.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

124
index.html Normal file
View file

@ -0,0 +1,124 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Rock Paper Scissors Card Game</title>
<link
rel="stylesheet"
href="https://pyscript.net/releases/2025.3.1/core.css"
/>
<script
type="module"
src="https://pyscript.net/releases/2025.3.1/core.js"
></script>
</head>
<body>
<h1>Rock Paper Scissors Card Game</h1>
<p>
Play Rock Paper Scissors with a twist! Use a 52-card deck with an
uneven distribution of cards. Both you and the AI get three cards,
and the AI selects randomly. The rules follow the classic game, but
with an added element of chance!
</p>
<p>Select a card to play against the AI! Best out of 5 game mode.</p>
<h3>Score</h3>
<p>
You: <span id="user-score">0</span> - AI:
<span id="ai-score">0</span>
</p>
<h3 id="result">Generating deck, please wait...</h3>
<style>
.card-button {
width: 120px; /* Keep button size */
height: 120px;
background-size: contain; /* Ensures full image fits */
background-repeat: no-repeat; /* Prevents tiling */
background-position: center; /* Centers image */
border: none;
cursor: pointer;
border-radius: 10px;
box-shadow: 3px 3px 8px rgba(0, 0, 0, 0.5);
}
/* Default unknown card */
#card-0,
#card-1,
#card-2 {
background-image: url("images/card-back.png");
}
/* Reset button */
#reset-game {
margin-top: 15px;
width: 100px;
height: 50px;
font-size: 16px;
border-radius: 8px;
background-color: red;
color: white;
cursor: pointer;
}
</style>
<div id="card-buttons">
<button
id="card-0"
index="0"
py-click="game.play_card"
class="card-button"
></button>
<button
id="card-1"
index="1"
py-click="game.play_card"
class="card-button"
></button>
<button
id="card-2"
index="2"
py-click="game.play_card"
class="card-button"
></button>
</div>
<button id="reset-game" py-click="game.reset_game">Reset Game</button>
<div
id="attribution"
style="margin-top: 20px; font-size: 14px; text-align: center"
>
<p><strong>Attribution for the used icons:</strong></p>
<a
href="https://www.flaticon.com/free-icons/playing-card"
title="playing card icons"
>Playing card icons by ArtBit - Flaticon</a
><br />
<a
href="https://www.flaticon.com/free-icons/rock-paper-scissors"
title="rock paper scissors icons"
>Rock paper scissors icons by iconading - Flaticon</a
><br />
<a
href="https://www.flaticon.com/free-icons/hands-and-gestures"
title="hands and gestures icons"
>Hands and gestures icons by Voysla - Flaticon</a
><br />
<a
href="https://www.flaticon.com/free-icons/fist"
title="fist icons"
>Fist icons by Cuputo - Flaticon</a
><br />
<a
href="https://www.flaticon.com/free-icons/rock-paper-scissors"
title="rock-paper-scissors icons"
>Rock-paper-scissors icons created by Freepik - Flaticon</a
><br />
</div>
<script type="py" src="./main.py"></script>
</body>
</html>

View file

@ -6,24 +6,10 @@ class RPSGame:
self.user_score = 0
self.ai_score = 0
self.deck = self.build_deck()
self.user_hand, self.ai_hand = self.give_cards()
self.refresh_hand()
self.rounds_to_win = (best_of // 2) + 1
self.update_ui()
def build_deck(self):
cards = [self.rock, self.paper, self.scissors]
deck = []
random.shuffle(cards)
for i in range(2):
n = random.randint(5, 20)
deck = cards[i](deck, n)
missing_cards = 52 - len(deck)
deck = cards[2](deck, missing_cards)
random.shuffle(deck)
return deck
def rock(self, lst, n):
lst.extend(["Rock"] * n)
return lst
@ -36,6 +22,35 @@ class RPSGame:
lst.extend(["Scissors"] * n)
return lst
def generate_deck(self):
cards = [self.rock, self.paper, self.scissors]
deck = []
random.shuffle(cards) # This is done, so the card types are not added in the same order everytime, is scissors is last everytime it could result in scissors always being low or high since the last card just are all sofar minus 52¨
for i in range(2):
n = random.randint(5, 20)
deck = cards[i](deck, n)
missing_cards = 52 - len(deck)
deck = cards[2](deck, missing_cards)
random.shuffle(deck)
return deck
def check_deck(self, lst):
card_types = ["Rock", "Paper", "Scissors"]
for card_type in card_types:
if lst.count(card_type) >= 25:
return False
return True
def build_deck(self):
acceptable_deck = False
while not acceptable_deck:
deck = self.generate_deck()
acceptable_deck = self.check_deck(deck)
return deck
def give_cards(self):
return [self.deck.pop(0) for _ in range(3)], [self.deck.pop(0) for _ in range(3)]
@ -52,10 +67,10 @@ class RPSGame:
self.user_hand, self.ai_hand = self.give_cards()
print("New cards")
print(f"AI cards: {self.ai_hand}")
print(f"{len(self.deck)} Cards left.")
def play_card(self, event):
index = int(event.target.getAttribute("index"))
#print(f"Button {index} clicked!") # Debugging
if index >= len(self.user_hand):
return
@ -80,16 +95,14 @@ class RPSGame:
pass
else:
if self.user_score < self.ai_score:
round_result = "You lost noob D:"
round_result = "You lost D: Better luck next time"
self.update_ui(round_result)
self.disable_buttons()
#self.reset_game()
return
else:
round_result = "You won great :D"
self.update_ui(round_result)
self.disable_buttons()
#self.reset_game()
return
self.update_ui(round_result)
@ -103,16 +116,18 @@ class RPSGame:
for i in range(3):
btn = document.querySelector(f"#card-{i}")
if i < len(self.user_hand):
btn.innerText = self.user_hand[i]
card_name = self.user_hand[i].lower() # Convert "Rock" -> "rock"
btn.style.backgroundImage = f"url('images/{card_name}.png')"
btn.disabled = False
else:
btn.innerText = "?"
btn.style.backgroundImage = "url('images/card-back.png')" # Unknown card
btn.disabled = True
def disable_buttons(self):
print("disable button function")
for i in range(3):
btn = document.querySelector(f"#card-{i}")
btn.innerText = "?"
btn.style.backgroundImage = "url('images/card-back.png')"
btn.disabled = True
def reset_game(self, event=None):
@ -120,13 +135,11 @@ class RPSGame:
self.user_score = 0
self.ai_score = 0
self.deck = self.build_deck()
self.user_hand, self.ai_hand = self.give_cards()
self.refresh_hand()
self.deck_overview()
self.update_ui("Game reset! Pick a card to start.")
def deck_overview(self):
#print(f"{len(self.deck)} Cards left")
print(f"{self.deck.count('Rock')} Rock cards")
print(f"{self.deck.count('Paper')} Paper cards")
print(f"{self.deck.count('Scissors')} Scissors cards")

View file

@ -1,44 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Rock Paper Scissors Card Game</title>
<link
rel="stylesheet"
href="https://pyscript.net/releases/2025.3.1/core.css"
/>
<script
type="module"
src="https://pyscript.net/releases/2025.3.1/core.js"
></script>
</head>
<body>
<h1>Rock Paper Scissors Card Game</h1>
<p>
Play Rock Paper Scissors with a twist! Use a 52-card deck with an
uneven distribution of cards. Both you and the AI get three cards,
and the AI selects randomly. The rules follow the classic game, but
with an added element of chance!
</p>
<p>Select a card to play against the AI! Best out of 5 game mode.</p>
<h3>Score</h3>
<p>
You: <span id="user-score">0</span> - AI:
<span id="ai-score">0</span>
</p>
<h3 id="result">Waiting for your move...</h3>
<div id="card-buttons">
<button id="card-0" index="0" py-click="game.play_card">?</button>
<button id="card-1" index="1" py-click="game.play_card">?</button>
<button id="card-2" index="2" py-click="game.play_card">?</button>
</div>
<p></p>
<button id="reset-game" py-click="game.reset_game">Reset Game</button>
<script type="py" src="./main.py"></script>
</body>
</html>

View file

@ -1,98 +0,0 @@
import random
class RPSGame:
def __init__(self, best_of = 3):
self.deck = self.build_deck()
self.user_score = 0
self.ai_score = 0
self.rounds_to_win = (best_of // 2) + 1
def build_deck(self):
cards = [self.rock, self.paper, self.scissors]
deck = []
random.shuffle(cards)
for i in range(2):
n = random.randint(5, 20)
deck = cards[i](deck, n)
missing_cards = 52 - len(deck)
deck = cards[2](deck, missing_cards)
random.shuffle(deck)
return deck
def rock(self, lst, n):
lst.extend(["Rock"] * n)
return lst
def paper(self, lst, n):
lst.extend(["Paper"] * n)
return lst
def scissors(self, lst, n):
lst.extend(["Scissors"] * n)
return lst
def give_cards(self):
return [self.deck.pop(0) for _ in range(3)], [self.deck.pop(0) for _ in range(3)]
def battle(self, user_card, ai_card):
if user_card == ai_card:
return 0
if (user_card == "Rock" and ai_card == "Scissors") or \
(user_card == "Paper" and ai_card == "Rock") or \
(user_card == "Scissors" and ai_card == "Paper"):
return 1
return -1
def play_cards(self, user_hand, ai_hand):
while user_hand and ai_hand:
print(f"\nYour cards: {user_hand}")
while True:
try:
user_choice = int(input(f"Select a card (1-{len(user_hand)}): ")) - 1
if 0 <= user_choice < len(user_hand):
break
except ValueError:
pass
print("Invalid choice. Try again.")
user_card = user_hand.pop(user_choice)
ai_card = ai_hand.pop(random.randint(0, len(ai_hand) - 1))
print(f"You played: {user_card}, AI played: {ai_card}")
result = self.battle(user_card, ai_card)
if result == 1:
print("You win this round!")
return 1
elif result == -1:
print("AI wins this round!")
return -1
else:
print("It's a tie! Play again with remaining cards.")
print("No cards left, this round is a draw.")
return 0
def play_game(self):
while self.user_score < self.rounds_to_win and self.ai_score < self.rounds_to_win and len(self.deck) >= 6:
user_hand, ai_hand = self.give_cards()
round_result = self.play_cards(user_hand, ai_hand)
if round_result == 1:
self.user_score += 1
elif round_result == -1:
self.ai_score += 1
print(f"Score - You: {self.user_score}, AI: {self.ai_score}")
if self.user_score > self.ai_score:
print("\n🎉 You won the match!")
else:
print("\n🤖 AI won the match!")
if __name__ == "__main__":
game = RPSGame(best_of = 3)
game.play_game()