Various fixes
This commit is contained in:
parent
80c6220f77
commit
e94259e614
|
@ -16,6 +16,7 @@ import logging
|
|||
from dataclasses import dataclass, field
|
||||
from enum import Enum, auto
|
||||
from typing import Optional
|
||||
from matplotlib.colors import same_color
|
||||
from pyrogram.types import Message
|
||||
from pyrogram.errors import RPCError
|
||||
from pyrogram.enums import ChatType
|
||||
|
@ -71,6 +72,7 @@ class UNOCard:
|
|||
placed_by: "UNOPlayer" = None
|
||||
# Extra metadata
|
||||
metadata: Optional[dict[str, int | str | bool]] = None
|
||||
placed_when: int = -1
|
||||
|
||||
|
||||
def get_default_deck(n_decks: int = 1) -> deque[UNOCard]:
|
||||
|
@ -80,7 +82,7 @@ def get_default_deck(n_decks: int = 1) -> deque[UNOCard]:
|
|||
"""
|
||||
|
||||
result = []
|
||||
for __ in range(n_decks):
|
||||
for __ in range(0, n_decks, 1):
|
||||
# Numbers: 1 to 9 for each color (twice for each color)
|
||||
for color in [UNOCardColor.GREEN, UNOCardColor.RED,
|
||||
UNOCardColor.YELLOW, UNOCardColor.BLUE]:
|
||||
|
@ -104,9 +106,9 @@ def get_default_deck(n_decks: int = 1) -> deque[UNOCard]:
|
|||
result.append(UNOCard(UNOCardType.DRAW, color, metadata={"exhausted": False}))
|
||||
# Wild and wild draw cards
|
||||
for _ in range(4):
|
||||
result.append(UNOCard(UNOCardType.WILD, UNOCardColor.BLACK, stackable=False, metadata={"exhausted": False}))
|
||||
result.append(UNOCard(UNOCardType.WILD, UNOCardColor.BLACK, stackable=False, metadata={"exhausted": False, "color": None}))
|
||||
for _ in range(4):
|
||||
result.append(UNOCard(UNOCardType.WILD_DRAW, UNOCardColor.BLACK, stackable=False, metadata={"exhausted": False}))
|
||||
result.append(UNOCard(UNOCardType.WILD_DRAW, UNOCardColor.BLACK, stackable=False, metadata={"exhausted": False, "color": None}))
|
||||
random.shuffle(result)
|
||||
return deque(result)
|
||||
|
||||
|
@ -139,6 +141,7 @@ class UNOGame:
|
|||
current_pos: int = 0
|
||||
current_player: Optional[UNOPlayer] = None
|
||||
winners: list[UNOPlayer] = field(default_factory=list)
|
||||
round: int = 0
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -517,7 +520,7 @@ def pick_first_card(game: UNOGame):
|
|||
break
|
||||
|
||||
|
||||
def distribute_cards(game: UNOGame, n: int = 1):
|
||||
def distribute_cards(game: UNOGame, n: int = 7):
|
||||
"""
|
||||
Distributes n cards to all players evenly
|
||||
as if they were being given in real life
|
||||
|
@ -528,9 +531,6 @@ def distribute_cards(game: UNOGame, n: int = 1):
|
|||
while not all(len(player.cards) == n for player in game.players):
|
||||
for player in game.players:
|
||||
player.cards.append(game.deck.popleft())
|
||||
for player in game.players:
|
||||
for card in player.cards:
|
||||
card.placed_by = player
|
||||
|
||||
|
||||
@Client.on_message(filters.command("begin"))
|
||||
|
@ -604,6 +604,7 @@ def card_to_string(card: UNOCard) -> str:
|
|||
UNOCardColor.GREEN: "🟢",
|
||||
UNOCardColor.YELLOW: "🟡",
|
||||
}
|
||||
print(maps)
|
||||
result = ""
|
||||
match card.kind:
|
||||
case UNOCardType.NUMBER:
|
||||
|
@ -646,13 +647,80 @@ async def info(_: Client, message: Message):
|
|||
logging.error(f"An unexpected RPC error occurred: {type(rpc_error).__name__} -> {rpc_error}")
|
||||
|
||||
|
||||
async def check_special_cards(client: Client, game: UNOGame):
|
||||
def pick_next_player(game: UNOGame):
|
||||
"""
|
||||
Picks the next player in the queue and schedules
|
||||
their turn
|
||||
"""
|
||||
|
||||
while True:
|
||||
game.current_pos += 1
|
||||
game.current_player = game.players[game.current_pos % len(game.players)]
|
||||
if game.current_player.won:
|
||||
# Skip players who won already
|
||||
continue
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
async def check_special_cards(client: Client, game: UNOGame):
|
||||
"""
|
||||
Performs some checks on the effects of special cards and
|
||||
sends some messages to users to make them aware of any
|
||||
changes that occur
|
||||
"""
|
||||
|
||||
try:
|
||||
draw = 0
|
||||
cards: list[UNOCard] = []
|
||||
i = 0
|
||||
while game.table[i].kind in {UNOCardType.WILD, UNOCardType.WILD_DRAW,
|
||||
UNOCardType.REVERSE, UNOCardType.SKIP,
|
||||
UNOCardType.DRAW} and not game.table[i].metadata["exhausted"]:
|
||||
cards.append(game.table[i])
|
||||
i += 1
|
||||
for card in cards:
|
||||
card.metadata["exhausted"] = True
|
||||
match card.kind:
|
||||
case UNOCardType.WILD | UNOCardType.WILD_DRAW as k:
|
||||
await client.send_message(game.current_player.id, "Choose the color for the new top card between red, green and blue")
|
||||
USERS[game.table[0].placed_by][0] = PlayerAction.CHOOSING_COLOR
|
||||
found = False
|
||||
if k == UNOCardType.WILD_DRAW:
|
||||
for next_card in game.current_player.cards:
|
||||
if next_card.kind == UNOCardType.WILD_DRAW:
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
draw += 4
|
||||
case UNOCardType.REVERSE:
|
||||
for user in game.players:
|
||||
if user != card.placed_by:
|
||||
await client.send_message(user.id, "The direction of play reverses!")
|
||||
game.players = list(reversed(game.players))
|
||||
case UNOCardType.DRAW:
|
||||
found = False
|
||||
for next_card in game.current_player.cards:
|
||||
if next_card.kind == UNOCardType.DRAW:
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
draw += 2
|
||||
case UNOCardType.SKIP:
|
||||
for user in game.players:
|
||||
if user != card.placed_by:
|
||||
await client.send_message(user.id, "A turn has been skipped!")
|
||||
pick_next_player(game)
|
||||
if draw:
|
||||
await client.send_message(game.current_player.id, f"You are foced to draw {draw} cards!")
|
||||
for _ in range(draw):
|
||||
if not game.deck:
|
||||
reshuffle_table_in_deck(game)
|
||||
game.current_player.cards.append(game.deck.popleft())
|
||||
await client.send_message(game.current_player.id, f"You draw a card. It's {card_to_string(game.current_player.cards[-1])}")
|
||||
except RPCError as rpc_error:
|
||||
logging.error(f"An unexpected RPC error occurred: {type(rpc_error).__name__} -> {rpc_error}")
|
||||
|
||||
|
||||
@Client.on_message(filters.command("pass"))
|
||||
async def pass_turn(client: Client, message: Message):
|
||||
|
@ -663,6 +731,8 @@ async def pass_turn(client: Client, message: Message):
|
|||
try:
|
||||
if (data := USERS[message.from_user.id])[0] == PlayerAction.NONE:
|
||||
await message.reply_text("You haven't created or joined a game lobby yet. Send /play to create it or /join <id> to join an existing one", parse_mode=ParseMode.MARKDOWN)
|
||||
elif data[0] == PlayerAction.CHOOSING_COLOR:
|
||||
await message.reply_text("You need to choose a color between red, green, blue and yellow before passing your turn")
|
||||
else:
|
||||
lobby = data[1]
|
||||
for player in lobby.game.players:
|
||||
|
@ -678,17 +748,8 @@ async def pass_turn(client: Client, message: Message):
|
|||
await message.reply_text("You have to draw or throw a card before passing your turn")
|
||||
return
|
||||
else:
|
||||
while True:
|
||||
if lobby.game.direction == UNODirection.CLOCKWISE:
|
||||
lobby.game.current_pos += 1
|
||||
else:
|
||||
lobby.game.current_pos -= 1
|
||||
lobby.game.current_player = lobby.game.players[lobby.game.current_pos % len(lobby.game.players)]
|
||||
if lobby.game.current_player.won:
|
||||
# Skip players who won already
|
||||
continue
|
||||
else:
|
||||
break
|
||||
pick_next_player(lobby.game)
|
||||
lobby.game.round += 1
|
||||
USERS[message.from_user.id] = (PlayerAction.END_TURN, USERS[message.from_user.id][1])
|
||||
name = f"{message.from_user.first_name or ''}{message.from_user.last_name or ''}"
|
||||
next_name = lobby.game.current_player.name
|
||||
|
@ -761,24 +822,31 @@ def check_next_throw(game: UNOGame, card: UNOCard) -> bool:
|
|||
"""
|
||||
|
||||
top = game.table[0]
|
||||
result = False
|
||||
match card.kind:
|
||||
case UNOCardType.NUMBER as k if top.kind == k:
|
||||
if card.metadata["value"] == top.metadata["value"]:
|
||||
result = True
|
||||
elif card.color == top.color:
|
||||
result = True
|
||||
# Can't stack colors!
|
||||
if top.placed_by and card.color == top.color and card.placed_by is top.placed_by:
|
||||
result = False
|
||||
case UNOCardType.DRAW | UNOCardType.REVERSE | UNOCardType.SKIP as k:
|
||||
result = top.kind == k and top.color == card.color
|
||||
top_color = top.color
|
||||
card_color = card.color
|
||||
if top.kind in {UNOCardType.WILD, UNOCardType.WILD_DRAW}:
|
||||
top_color = top.metadata["color"]
|
||||
if card.kind in {UNOCardType.WILD, UNOCardType.WILD_DRAW}:
|
||||
card_color = card.metadata["color"]
|
||||
same_color = card_color == top_color
|
||||
same_turn = top.placed_when == game.round
|
||||
match top.kind:
|
||||
case UNOCardType.NUMBER as k if card.kind == k:
|
||||
# Same number or same color in this turn
|
||||
return top.metadata["value"] == card.metadata["value"] or (same_color and not same_turn)
|
||||
case UNOCardType.NUMBER as k if card.kind != k:
|
||||
# Same as above, without the number check
|
||||
return same_color and not same_turn
|
||||
case UNOCardType.SKIP | UNOCardType.REVERSE | UNOCardType.DRAW:
|
||||
result = same_color and not same_turn
|
||||
if top.kind == card.kind:
|
||||
# Can you stack this special card in a turn?
|
||||
result = result and top.stackable
|
||||
return result
|
||||
case UNOCardType.WILD | UNOCardType.WILD_DRAW:
|
||||
result = True
|
||||
return False # TODO
|
||||
case _:
|
||||
result = False
|
||||
result = result and top.stackable
|
||||
return result
|
||||
return False
|
||||
|
||||
|
||||
@Client.on_message(filters.command("throw"))
|
||||
|
@ -815,6 +883,8 @@ async def throw(client: Client, message: Message):
|
|||
if card_no > len(player.cards) or card_no == 0:
|
||||
await message.reply_text(f"Invalid card: you can choose between 1 and {len(player.cards)}")
|
||||
return
|
||||
player.cards[card_no - 1].placed_when = lobby.game.round
|
||||
player.cards[card_no - 1].placed_by = player
|
||||
if not check_next_throw(lobby.game, player.cards[card_no - 1]):
|
||||
await message.reply_text("You can't throw that card")
|
||||
return
|
||||
|
@ -864,6 +934,6 @@ async def throw(client: Client, message: Message):
|
|||
msg = f"Game status: \n- Card on the table: {card_to_string(lobby.game.table[0])}\n\n- Current Player: {lobby.game.current_player.name}\n\n- Your cards:\n"
|
||||
for i, card in enumerate(player.cards):
|
||||
msg += f"{i + 1}. {card_to_string(card)}\n"
|
||||
await client.send_message(lobby.game.current_player.id, msg)
|
||||
await client.send_message(lobby.game.current_player.id, msg)
|
||||
except RPCError as rpc_error:
|
||||
logging.error(f"An unexpected RPC error occurred: {type(rpc_error).__name__} -> {rpc_error}")
|
||||
|
|
Loading…
Reference in New Issue