diff --git a/Bot.py b/Bot.py
index aa97467..d5454cc 100644
--- a/Bot.py
+++ b/Bot.py
@@ -327,29 +327,121 @@ async def on_thread_update(
async def cmd_flip(ctx: misc.BotContext, bet: str = "10") -> int:
if misc.ctx_created_thread(ctx):
return -1
- report = (
- bucks.FinMsg.format(ctx.author.mention)
- if bucks.active_game(BlackjackGames, ctx.author)
- else bucks.flip(ctx.author, bet.lower())
- )
+ if "," in ctx.author.name:
+ report = bucks.CommaWarn.format(ctx.author.mention)
+ else:
+ report = (
+ bucks.FinMsg.format(ctx.author.mention)
+ if bucks.player_in_game(BlackjackGames, ctx.author)
+ else bucks.flip(ctx.author, bet.lower())
+ )
await ctx.send(embed=misc.bb_embed("Beardless Bot Coin Flip", report))
return 1
+# NOTE: duplicate code
@BeardlessBot.command(name="blackjack", aliases=("bj",))
async def cmd_blackjack(ctx: misc.BotContext, bet: str = "10") -> int:
if misc.ctx_created_thread(ctx):
return -1
- if bucks.active_game(BlackjackGames, ctx.author):
+ if "," in ctx.author.name:
+ report = bucks.CommaWarn.format(ctx.author.mention)
+ elif bucks.player_in_game(BlackjackGames, ctx.author):
+ report = bucks.FinMsg.format(ctx.author.mention)
+ else:
+ can_bet, bet_report = bucks.can_make_bet(ctx.author, bet)
+ if not can_bet:
+ assert bet_report is not None
+ report = bet_report
+ else:
+ report, game = bucks.blackjack(ctx.author, bet)
+ if game and not game.round_over():
+ BlackjackGames.append(game)
+ await ctx.send(embed=misc.bb_embed("Beardless Bot Blackjack", report))
+ return 1
+
+
+@BeardlessBot.command(name="tableleave")
+async def cmd_tableleave(ctx: misc.BotContext) -> int:
+ if misc.ctx_created_thread(ctx):
+ return -1
+ if result := bucks.player_in_game(BlackjackGames, ctx.author):
+ game, player = result
+ if not game.multiplayer:
+ report = (
+ "You can't exit a singlelpayer Blackjack Game"
+ f"{ctx.author.mention}\n"
+ )
+ elif game.started:
+ report = "Cannot leave mid-round. Please wait for the round to end."
+ elif len(game.players) == 1:
+ BlackjackGames.remove(game)
+ report = "Game disbanded.\n"
+ elif player == game.owner:
+ assert game.owner == game.players[0]
+ game.players.remove(player)
+ game.owner = game.players[0]
+ report = (
+ f"You left. {game.owner.name.mention} "
+ "you are now the owner of the game.\n"
+ )
+ else:
+ game.players.remove(player)
+ report = "You left.\n"
+ else:
+ report = bucks.NoMultiplayerGameMsg.format(ctx.author.mention)
+ await ctx.send(embed=misc.bb_embed("Beardless Bot Blackjack", report))
+ return 1
+
+
+# NOTE: duplicate code
+@BeardlessBot.command(name="tablenew")
+async def cmd_tablenew(ctx: misc.BotContext) -> int:
+ if misc.ctx_created_thread(ctx):
+ return -1
+ if "," in ctx.author.name:
+ report = bucks.CommaWarn.format(ctx.author.mention)
+ if bucks.player_in_game(BlackjackGames, ctx.author):
report = bucks.FinMsg.format(ctx.author.mention)
else:
- report, game = bucks.blackjack(ctx.author, bet)
+ report, game = bucks.blackjack(ctx.author, None)
if game:
BlackjackGames.append(game)
await ctx.send(embed=misc.bb_embed("Beardless Bot Blackjack", report))
return 1
+@BeardlessBot.command(name="tablebet")
+async def cmd_tablebet(ctx: misc.BotContext, bet: str = "10") -> int:
+ if misc.ctx_created_thread(ctx):
+ return -1
+ report: str | None
+ if "," in ctx.author.name:
+ report = bucks.CommaWarn.format(ctx.author.mention)
+ else:
+ report = bucks.NoMultiplayerGameMsg.format(ctx.author.mention)
+ if result := bucks.player_in_game(BlackjackGames, ctx.author):
+ game, player = result
+ if game.multiplayer:
+ if game.started:
+ report = "Cannot change bet mid-round."
+ else:
+ can_bet, report = bucks.can_make_bet(ctx.author, bet)
+ if can_bet:
+ report, bet_number = bucks.make_bet(
+ ctx.author,
+ game, bet,
+ )
+ report = (
+ "Your current bet is "
+ f"{bet_number}\n{ctx.author.mention}"
+ )
+ player.bet = bet_number
+ assert report is not None
+ await ctx.send(embed=misc.bb_embed("Beardless Bot Blackjack", report))
+ return 1
+
+
@BeardlessBot.command(name="deal", aliases=("hit",))
async def cmd_deal(ctx: misc.BotContext) -> int:
if misc.ctx_created_thread(ctx):
@@ -358,18 +450,85 @@ async def cmd_deal(ctx: misc.BotContext) -> int:
report = bucks.CommaWarn.format(ctx.author.mention)
else:
report = bucks.NoGameMsg.format(ctx.author.mention)
- if game := bucks.active_game(BlackjackGames, ctx.author):
- report = game.deal_to_player()
- if game.check_bust() or game.perfect():
- game.check_bust()
- bucks.write_money(
- ctx.author, game.bet, writing=True, adding=True,
- )
- BlackjackGames.remove(game)
+ if result := bucks.player_in_game(BlackjackGames, ctx.author):
+ game, player = result
+ if not game.started:
+ report = "Game has not started yet"
+ elif not game.is_turn(player):
+ report = f"It is not your turn {ctx.author.mention}"
+ else:
+ assert game.dealerUp is not None
+ report = game.deal_current_player()
+ if (
+ (player.check_bust() or player.perfect())
+ and not game.multiplayer
+ ):
+ BlackjackGames.remove(game)
+ await ctx.send(embed=misc.bb_embed("Beardless Bot Blackjack", report))
+ return 1
+
+
+@BeardlessBot.command(name="tablestart")
+async def cmd_tablestart(ctx: misc.BotContext) -> int:
+ if misc.ctx_created_thread(ctx):
+ return -1
+ report = bucks.NoGameMsg.format(ctx.author.mention)
+ if result := bucks.player_in_game(BlackjackGames, ctx.author):
+ game, player = result
+ if game.owner is not player:
+ report = "You are not the owner of this table"
+ elif not game.ready_to_start():
+ report = "Not all players have made their bets"
+ else:
+ report = "Match started\n"
+ report += game.start_game()
await ctx.send(embed=misc.bb_embed("Beardless Bot Blackjack", report))
return 1
+@BeardlessBot.command(name="tablejoin")
+async def cmd_tablejoin(
+ ctx: misc.BotContext,
+ target: str | None = None,
+) -> int:
+ if misc.ctx_created_thread(ctx) or not ctx.guild:
+ return -1
+ if "," in ctx.author.name:
+ report = bucks.CommaWarn.format(ctx.author.mention)
+ else:
+ if not (join_target := await misc.process_command_target(
+ ctx, target, BeardlessBot,
+ )):
+ return 0
+ if result := bucks.player_in_game(BlackjackGames, ctx.author):
+ report = bucks.FinMsg.format(ctx.author.mention)
+ elif result := bucks.player_in_game(BlackjackGames, join_target):
+ game, _ = result
+ if game.multiplayer:
+ if game.started:
+ game.add_player(ctx.author)
+ report = f"Joined {join_target.mention}'s blackjack game."
+ else:
+ report = (
+ f"Cannot join {join_target.mention}'s "
+ "blackjack game mid-round. "
+ "Please wait for the round to end."
+ )
+ else:
+ report = (
+ f"Can't join {join_target.mention}'s "
+ "singleplayer blackjack game."
+ )
+ else:
+ report = f"Player {join_target.mention} is not in a blackjack game"
+ await ctx.send(embed=misc.bb_embed("Beardless Bot Join", report))
+ # if channel := misc.get_log_channel(ctx.guild):
+ # await channel.send(embed=logs.log_mute(
+ # join_target, ctx.message, duration,
+ # ))
+ return 1
+
+
@BeardlessBot.command(name="stay", aliases=("stand",))
async def cmd_stay(ctx: misc.BotContext) -> int:
if misc.ctx_created_thread(ctx):
@@ -378,17 +537,16 @@ async def cmd_stay(ctx: misc.BotContext) -> int:
report = bucks.CommaWarn.format(ctx.author.mention)
else:
report = bucks.NoGameMsg.format(ctx.author.mention)
- if game := bucks.active_game(BlackjackGames, ctx.author):
- result = game.stay()
- report = game.message
- if result and game.bet:
- written, bonus = bucks.write_money(
- ctx.author, game.bet, writing=True, adding=True,
- )
- if written == bucks.MoneyFlags.CommaInUsername:
- assert isinstance(bonus, str)
- report = bonus
- BlackjackGames.remove(game)
+ if result := bucks.player_in_game(BlackjackGames, ctx.author):
+ game, player = result
+ if not game.started:
+ report = "Game has not started yet"
+ elif not game.is_turn(player):
+ report = f"It is not your turn {ctx.author.mention}"
+ else:
+ report = game.stay_current_player()
+ if not game.multiplayer:
+ BlackjackGames.remove(game)
await ctx.send(embed=misc.bb_embed("Beardless Bot Blackjack", report))
return 1
@@ -434,9 +592,14 @@ async def cmd_balance(ctx: misc.BotContext, *, target: str = "") -> int:
async def cmd_leaderboard(ctx: misc.BotContext, *, target: str = "") -> int:
if misc.ctx_created_thread(ctx):
return -1
- await ctx.send(
- embed=bucks.leaderboard(misc.get_target(ctx, target), ctx.message),
- )
+ if "," in ctx.author.name:
+ embed = misc.bb_embed(
+ "BeardlessBot Comma Warn",
+ bucks.CommaWarn.format(ctx.author.mention),
+ )
+ else:
+ embed = bucks.leaderboard(misc.get_target(ctx, target), ctx.message)
+ await ctx.send(embed=embed)
return 1
@@ -469,7 +632,21 @@ async def cmd_reset(ctx: misc.BotContext) -> int:
"""
if misc.ctx_created_thread(ctx):
return -1
- await ctx.send(embed=bucks.reset(ctx.author))
+ if "," in ctx.author.name:
+ report = bucks.CommaWarn.format(ctx.author.mention)
+ else:
+ game = None
+ if result := bucks.player_in_game(BlackjackGames, ctx.author):
+ game, player = result
+ if game is None:
+ report = bucks.reset(ctx.author)
+ elif game.multiplayer and not game.started:
+ player.bet = 10 # TODO: move the default bet to a variable
+ report = bucks.reset(ctx.author)
+ report += " Your bet has also been reset to 10."
+ else:
+ report = bucks.FinMsg.format(ctx.author.mention)
+ await ctx.send(embed=misc.bb_embed("BeardlessBucks Reset", report))
return 1
@@ -845,7 +1022,7 @@ async def cmd_buy(
else:
if not role.color.value:
await role.edit(colour=nextcord.Colour(RoleColors[color]))
- result, bonus = bucks.write_money(
+ result, _ = bucks.write_money(
ctx.author, -50000, writing=True, adding=True,
)
if result == bucks.MoneyFlags.BalanceChanged:
@@ -853,9 +1030,6 @@ async def cmd_buy(
"Color " + role.mention + " purchased successfully, {}!"
)
await ctx.author.add_roles(role)
- elif result == bucks.MoneyFlags.CommaInUsername:
- assert isinstance(bonus, str)
- report = bonus
elif result == bucks.MoneyFlags.Registered:
report = bucks.NewUserMsg
else:
diff --git a/bb_test.py b/bb_test.py
index c64495c..35a55ae 100644
--- a/bb_test.py
+++ b/bb_test.py
@@ -2379,11 +2379,6 @@ async def test_cmd_register() -> None:
assert m is not None
assert m.embeds[0].description == emb.description
- bb._user.name = ",badname,"
- assert bucks.register(bb).description == (
- bucks.CommaWarn.format(f"<@{misc.BbId}>")
- )
-
@MarkAsync
@pytest.mark.parametrize(
@@ -2393,7 +2388,6 @@ async def test_cmd_register() -> None:
MockMember(MockUser("Test", "5757", misc.BbId)),
"'s balance is 200",
),
- (MockMember(MockUser(",")), bucks.CommaWarn.format("<@123456789>")),
],
)
async def test_cmd_balance(target: nextcord.User, result: str) -> None:
@@ -2420,15 +2414,10 @@ def test_reset() -> None:
MockUser("Beardless Bot", discriminator="5757", user_id=misc.BbId),
"Beardless Bot",
)
- assert bucks.reset(bb).description == (
+ assert bucks.reset(bb) == (
f"You have been reset to 200 BeardlessBucks, <@{misc.BbId}>."
)
- bb._user.name = ",badname,"
- assert bucks.reset(bb).description == (
- bucks.CommaWarn.format(f"<@{misc.BbId}>")
- )
-
def test_write_money() -> None:
bb = MockMember(
@@ -2442,7 +2431,7 @@ def test_write_money() -> None:
assert bucks.write_money(
bb, -1000000, writing=True, adding=False,
- ) == (bucks.MoneyFlags.NotEnoughBucks, None)
+ ) == (bucks.MoneyFlags.NotEnoughBucks, 200)
def test_leaderboard() -> None:
@@ -2453,12 +2442,6 @@ def test_leaderboard() -> None:
assert lb.fields[1].value is not None
assert int(lb.fields[0].value) > int(lb.fields[1].value)
- lb = bucks.leaderboard(
- MockMember(MockUser(name="bad,name", user_id=0)),
- MockMessage(author=MockMember()),
- )
- assert len(lb.fields) == 10
-
lb = bucks.leaderboard(
MockMember(
MockUser("Beardless Bot", discriminator="5757", user_id=misc.BbId),
@@ -2593,7 +2576,7 @@ def test_flip() -> None:
mp.setattr("random.randint", lambda *_: 0)
assert bucks.flip(bb, "all") == (
"Tails! You lose! Your losses have been"
- f" deducted from your balance, <@{misc.BbId}>."
+ f" deducted from your balance, <@{misc.BbId}>.\n"
)
msg = bucks.balance(bb, MockMessage("!bal", bb))
assert isinstance(msg.description, str)
@@ -2612,7 +2595,7 @@ def test_flip() -> None:
mp.setattr("random.randint", lambda *_: 1)
assert bucks.flip(bb, "all") == (
"Heads! You win! Your winnings have been"
- f" added to your balance, <@{misc.BbId}>."
+ f" added to your balance, <@{misc.BbId}>.\n"
)
msg = bucks.balance(bb, MockMessage("!bal", bb))
assert isinstance(msg.description, str)
@@ -2634,9 +2617,6 @@ def test_flip() -> None:
bucks.NewUserMsg.format(f"<@{misc.BbId}>")
)
- bb._user.name = ",invalidname,"
- assert bucks.flip(bb, "0") == bucks.CommaWarn.format(f"<@{misc.BbId}>")
-
@MarkAsync
async def test_cmd_flip() -> None:
@@ -2655,7 +2635,7 @@ async def test_cmd_flip() -> None:
assert emb.description is not None
assert emb.description.endswith("actually bet anything.")
- Bot.BlackjackGames.append(bucks.BlackjackGame(bb, 10))
+ Bot.BlackjackGames.append(bucks.BlackjackGame(bb, multiplayer=False))
assert await Bot.cmd_flip(ctx, bet="0") == 1
m = await latest_message(ctx)
assert m is not None
@@ -2671,14 +2651,15 @@ def test_blackjack() -> None:
bucks.reset(bb)
with pytest.MonkeyPatch.context() as mp:
- mp.setattr("bucks.BlackjackGame.perfect", lambda _: False)
+ mp.setattr("bucks.BlackjackPlayer.perfect", lambda _: False)
report, game = bucks.blackjack(bb, 0)
assert isinstance(game, bucks.BlackjackGame)
- assert "You hit 21!" not in report
+ assert "you hit 21!" not in report
with pytest.MonkeyPatch.context() as mp:
- mp.setattr("bucks.BlackjackGame.perfect", lambda _: True)
- report, game = bucks.blackjack(bb, "0")
+ mp.setattr("bucks.BlackjackPlayer.perfect", lambda _: True)
+ mp.setattr("random.randint", lambda x, _: x) # no dealer blackjack
+ report, game = bucks.blackjack(bb, 0)
assert game is None
assert "You hit 21" in report
@@ -2687,20 +2668,14 @@ def test_blackjack() -> None:
"bucks.write_money",
lambda *_, **__: (bucks.MoneyFlags.Registered, 0),
)
- assert bucks.blackjack(bb, "0")[0] == (
+ assert (
bucks.NewUserMsg.format(f"<@{misc.BbId}>")
- )
+ in bucks.blackjack(bb, "0")[0])
bucks.reset(bb)
report = bucks.blackjack(bb, "10000000000000")[0]
assert report.startswith("You do not have")
- bucks.reset(bb)
- bb._user.name = ",invalidname,"
- assert bucks.blackjack(bb, 0)[0] == (
- bucks.CommaWarn.format(f"<@{misc.BbId}>")
- )
-
@MarkAsync
async def test_cmd_blackjack() -> None:
@@ -2714,20 +2689,20 @@ async def test_cmd_blackjack() -> None:
m = await latest_message(ctx)
assert m is not None
assert m.embeds[0].description is not None
- assert m.embeds[0].description.startswith("Your starting hand consists of")
+ assert "your starting hand consists of" in m.embeds[0].description
with pytest.MonkeyPatch.context() as mp:
- mp.setattr("bucks.BlackjackGame.perfect", lambda _: True)
+ mp.setattr("bucks.BlackjackPlayer.perfect", lambda _: True)
Bot.BlackjackGames = []
assert await Bot.cmd_blackjack(ctx, bet="all") == 1
m = await latest_message(ctx)
assert m is not None
assert m.embeds[0].description is not None
assert m.embeds[0].description.endswith(
- f"You hit {bucks.BlackjackGame.Goal}! You win, {bb.mention}!",
+ f"You hit {bucks.BlackjackGame.Goal}! {bucks.WinMsg}.\n",
)
- Bot.BlackjackGames.append(bucks.BlackjackGame(bb, 10))
+ Bot.BlackjackGames.append(bucks.BlackjackGame(bb, multiplayer=False))
assert await Bot.cmd_blackjack(ctx, bet="0") == 1
m = await latest_message(ctx)
assert m is not None
@@ -2736,7 +2711,7 @@ async def test_cmd_blackjack() -> None:
@MarkAsync
-async def test_cmd_deal() -> None:
+async def test_cmd_deal1() -> None:
Bot.BlackjackGames = []
bb = MockMember(
MockUser("Beardless,Bot", discriminator="5757", user_id=misc.BbId),
@@ -2753,20 +2728,25 @@ async def test_cmd_deal() -> None:
assert m is not None
assert m.embeds[0].description == bucks.NoGameMsg.format(f"<@{misc.BbId}>")
- game = bucks.BlackjackGame(bb, 0)
- game.hand = [2, 2]
- Bot.BlackjackGames = []
- Bot.BlackjackGames.append(game)
+ game = bucks.BlackjackGame(bb, multiplayer=False)
+ assert len(game.players) == 1
+ player = game.players[0]
+ assert game.turn_idx == 0
+ player.hand = [2, 2]
+ Bot.BlackjackGames = [game]
assert await Bot.cmd_deal(ctx) == 1
m = await latest_message(ctx)
assert m is not None
emb = m.embeds[0]
- assert len(game.hand) == 3
assert emb.description is not None
- assert emb.description.startswith("You were dealt")
-
- game = bucks.BlackjackGame(bb, 0)
- game.hand = [10, 10, 10]
+ assert emb.description.startswith(f"{bb.mention} you were dealt")
+ assert len(player.hand) == 3
+
+ game = bucks.BlackjackGame(bb, multiplayer=False)
+ assert len(game.players) == 1
+ player = game.players[0]
+ player.bet = 0
+ player.hand = [10, 10, 10]
Bot.BlackjackGames = []
Bot.BlackjackGames.append(game)
assert await Bot.cmd_deal(ctx) == 1
@@ -2774,69 +2754,117 @@ async def test_cmd_deal() -> None:
assert m is not None
emb = m.embeds[0]
assert emb.description is not None
- assert f"You busted. Game over, <@{misc.BbId}>." in emb.description
+ assert "You busted. Game over" in emb.description
+ assert f"<@{misc.BbId}>" in emb.description
assert len(Bot.BlackjackGames) == 0
- game = bucks.BlackjackGame(bb, 0)
- game.hand = [10, 10]
+
+@MarkAsync
+async def test_cmd_deal2() -> None:
+ # your boy ruff doesn't like more than 50 stmts in functions
+ Bot.BlackjackGames = []
+ bb = MockMember(
+ MockUser("Beardless,Bot", discriminator="5757", user_id=misc.BbId),
+ )
+ ch = MockChannel(guild=MockGuild())
+ ctx = MockContext(
+ Bot.BeardlessBot, MockMessage("!hit"), ch, bb, MockGuild(),
+ )
+ Bot.BlackjackGames = []
+ bb = MockMember(
+ MockUser("Beardless,Bot", discriminator="5757", user_id=misc.BbId),
+ )
+ ch = MockChannel(guild=MockGuild())
+ ctx = MockContext(
+ Bot.BeardlessBot, MockMessage("!hit"), ch, bb, MockGuild(),
+ )
+ assert await Bot.cmd_deal(ctx) == 1
+ m = await latest_message(ch)
+ assert m is not None
+ assert m.embeds[0].description == bucks.CommaWarn.format(f"<@{misc.BbId}>")
+
+ bb._user.name = "Beardless Bot"
+ assert await Bot.cmd_deal(ctx) == 1
+ m = await latest_message(ch)
+ assert m is not None
+ assert m.embeds[0].description == bucks.NoGameMsg.format(f"<@{misc.BbId}>")
+
+ game = bucks.BlackjackGame(bb, multiplayer=False)
+ player = game.players[0]
+ player.bet = 0
+ player.hand = [10, 10]
Bot.BlackjackGames.append(game)
with pytest.MonkeyPatch.context() as mp:
- mp.setattr("bucks.BlackjackGame.perfect", lambda _: True)
- mp.setattr("bucks.BlackjackGame.check_bust", lambda _: False)
+ mp.setattr("bucks.BlackjackPlayer.perfect", lambda _: True)
+ mp.setattr("bucks.BlackjackPlayer.check_bust", lambda _: False)
assert await Bot.cmd_deal(ctx) == 1
m = await latest_message(ctx)
assert m is not None
emb = m.embeds[0]
assert emb.description is not None
- assert f"You hit 21! You win, <@{misc.BbId}>!" in emb.description
+ assert "You hit 21! You win" in emb.description
+ assert f"<@{misc.BbId}>" in emb.description
assert len(Bot.BlackjackGames) == 0
def test_blackjack_deal_to_player_treats_ace_as_1_when_going_over() -> None:
- game = bucks.BlackjackGame(MockMember(), 10)
- game.hand = [8, 11]
+ m = MockMember()
+ game = bucks.BlackjackGame(m, multiplayer=False)
+ player = game.players[0]
+ player.bet = 10
+ player.hand = [11, 9]
with pytest.MonkeyPatch.context() as mp:
- game.deck = [3, 4, 5]
+ game.deck = [2, 4, 5]
mp.setattr("random.randint", lambda x, _: x)
- game.deal_to_player()
- assert len(game.hand) == 3
- assert sum(game.hand) == 12
- assert game.message.startswith(
- "You were dealt a 3, bringing your total to 22. To avoid busting,"
+ assert game.dealerUp is not None
+ report = game.deal_current_player()
+ assert len(player.hand) == 3
+ assert sum(player.hand) == 12
+ assert report.startswith(
+ f"{player.name.mention} you were dealt a 2, "
+ "bringing your total to 22. To avoid busting,"
" your Ace will be treated as a 1. Your new total is 12.",
)
def test_blackjack_deal_to_player_wins_when_reaching_21() -> None:
m = MockMember()
- game = bucks.BlackjackGame(m, 10)
- game.hand = [10, 9]
+ game = bucks.BlackjackGame(m, multiplayer=False)
+ player = game.players[0]
+ player.bet = 10
+ player.hand = [10, 9]
+ assert game.dealerUp is not None
with pytest.MonkeyPatch.context() as mp:
game.deck = [2, 3, 4]
mp.setattr("random.randint", lambda x, _: x)
- game.deal_to_player()
- assert game.message.startswith(
- "You were dealt a 2, bringing your total to 21."
+ report = game.deal_current_player()
+ assert report.startswith(
+ f"{m.mention} you were dealt a 2, bringing your total to 21."
" Your card values are 10, 9, 2. The dealer is showing ",
)
- assert game.message.endswith(
- f", with one card face down. You hit 21! You win, {m.mention}!",
+ assert report.endswith(
+ ", with one card face down. You hit 21! "
+ f"{bucks.WinMsg}, {m.mention}.\n",
)
def test_blackjack_deal_top_card_pops_top_card() -> None:
with pytest.MonkeyPatch.context() as mp:
mp.setattr("random.randint", lambda x, _: x)
- game = bucks.BlackjackGame(MockMember(), 10)
+ m = MockMember()
+ game = bucks.BlackjackGame(m, multiplayer=False)
+ player = game.players[0]
+ player.bet = 10
# Two cards dealt to player, two to dealer
# Dealer dealt 2, 3; player dealt 4, 5
- assert len(game.deck) == 48
+ starting_deck_count = 13 * 4 * bucks.BlackjackGame.NumOfDecksInMatch
+ assert len(game.deck) == starting_deck_count - 4
assert game.dealerUp == 2
assert game.dealerSum == 5
- assert sum(game.hand) == 9
+ assert sum(player.hand) == 9
# Next card should be a 6
assert game.deal_top_card() == 6
- assert len(game.deck) == 47
+ assert len(game.deck) == starting_deck_count - 5
def test_blackjack_card_name() -> None:
@@ -2849,12 +2877,15 @@ def test_blackjack_card_name() -> None:
def test_blackjack_check_bust() -> None:
- game = bucks.BlackjackGame(MockMember(), 10)
- game.hand = [10, 10, 10]
- assert game.check_bust()
+ m = MockMember()
+ player = bucks.BlackjackPlayer(m)
+ bucks.BlackjackGame(m, multiplayer=False)
+ player.hand = [10, 10, 10]
+ player.bet = 10
+ assert player.check_bust()
- game.hand = [3, 4]
- assert not game.check_bust()
+ player.hand = [3, 4]
+ assert not player.check_bust()
@MarkAsync
@@ -2874,72 +2905,100 @@ async def test_cmd_stay() -> None:
def test_blackjack_stay() -> None:
with pytest.MonkeyPatch.context() as mp:
mp.setattr("random.randint", lambda x, _: x)
- member = MockMember()
- game = bucks.BlackjackGame(member, 0)
- game.hand = [10, 10, 1]
- game.dealerSum = 25
- assert game.stay() == 1
- assert game.message.endswith(
- f"to your balance, {member.mention}."
- " Unfortunately, you bet nothing, so this was all pointless.",
- )
-
- game.bet = 10
+ m = MockMember()
+
+ game = bucks.BlackjackGame(m, multiplayer=False)
+ player = game.players[0]
+ player.bet = 0
+ player.hand = [10, 1]
+ game.dealerSum = 13
+ game.dealerUp = 6
+ game.deck = [8]
+ game.stay_current_player()
+ assert game.round_over()
+ assert "you lose" in game._end_round().lower()
+
+ game = bucks.BlackjackGame(m, multiplayer=False)
+ player = game.players[0]
+ player.bet = 0
+ player.hand = [10, 1]
+ game.dealerSum = 12
+ game.dealerUp = 5
+ game.deck = [10, 10]
+ game.stay_current_player()
+ assert game.round_over()
+ assert "you win" in game._end_round().lower()
+
+ game = bucks.BlackjackGame(m, multiplayer=False)
+ player = game.players[0]
+ player.bet = 0
+ player.hand = [10, 10]
game.dealerSum = 20
- assert game.stay() == 1
- assert game.message.endswith(f"to your balance, {member.mention}.")
- game.deal_to_player()
- assert game.stay() == 1
- assert game.message.endswith(f"from your balance, {member.mention}.")
-
- game.hand = [10, 10]
- assert game.stay() == 0
-
- game.dealerSum = 14
- assert game.stay() == 1
+ game.dealerUp = 10
+ game.stay_current_player()
+ assert game.round_over()
+ assert "ties your sum" in game._end_round().lower()
def test_blackjack_starting_hand() -> None:
m = MockMember()
- game = bucks.BlackjackGame(m, 10)
- game.hand = []
- game.message = game.starting_hand()
- assert len(game.hand) == 2
- assert game.message.startswith("Your starting hand consists of ")
+ game = bucks.BlackjackGame(m, multiplayer=False)
+ player = game.players[0]
+ player.bet = 10
+ player.hand = []
+ game.message = game.start_game()
+ assert len(player.hand) == 2
+ assert f"{m.mention} your starting hand consists of " in game.message
+
+ player.hand = []
+ with pytest.MonkeyPatch.context() as mp:
+ mp.setattr("bucks.BlackjackPlayer.perfect", lambda _: False)
+ assert "You hit 21!" not in game.start_game()
+ assert len(player.hand) == 2
- game.hand = []
+ player.hand = []
with pytest.MonkeyPatch.context() as mp:
- mp.setattr("bucks.BlackjackGame.perfect", lambda _: False)
- assert "You hit 21!" not in game.starting_hand()
- assert len(game.hand) == 2
+ mp.setattr("bucks.BlackjackPlayer.perfect", lambda _: True)
+ mp.setattr("random.randint", lambda x, _: x) # for deck draws
+ game.deck = [
+ 2, 3, # no dealer blackjack
+ bucks.BlackjackGame.AceVal, bucks.BlackjackGame.FaceVal,
+ ]
+ report = game.start_game()
+ assert "You hit 21!" in report
+ assert len(player.hand) == 2
+
+ player.hand = []
- game.hand = []
with pytest.MonkeyPatch.context() as mp:
- mp.setattr("bucks.BlackjackGame.perfect", lambda _: True)
- assert "You hit 21!" in game.starting_hand()
- assert len(game.hand) == 2
-
- game.hand = []
- game.deck = [bucks.BlackjackGame.AceVal, bucks.BlackjackGame.AceVal]
- assert game.starting_hand() == (
- "Your starting hand consists of two Aces."
- " One of them will act as a 1. Your total is 12."
- " Type !hit to deal another card to yourself, or !stay"
- f" to stop at your current total, {m.mention}."
- )
- assert len(game.hand) == 2
- assert game.hand[1] == 1
+ mp.setattr("random.randint", lambda _, y: y)
+ game.deck = [
+ bucks.BlackjackGame.AceVal, bucks.BlackjackGame.AceVal,
+ 1, 2,
+ ]
+ assert game.start_game() == (
+ "The dealer is showing 2, with one card face down.\n"
+ f"{m.mention} your starting hand consists of two Aces."
+ " One of them will act as a 1. Your total is 12.\n"
+ "Type !hit to deal another card to yourself, or !stay"
+ f" to stop at your current total."
+ )
+ assert len(player.hand) == 2
+ assert player.hand[1] == 1
def test_active_game() -> None:
author = MockMember(MockUser(name="target", user_id=0))
games = [
- bucks.BlackjackGame(MockMember(MockUser(name="not", user_id=1)), 10),
+ bucks.BlackjackGame(
+ MockMember(MockUser(name="not", user_id=1)),
+ multiplayer=False,
+ ),
] * 9
- assert bucks.active_game(games, author) is None
+ assert bucks.player_in_game(games, author) is None
- games.append(bucks.BlackjackGame(author, 10))
- assert bucks.active_game(games, author)
+ games.append(bucks.BlackjackGame(author, multiplayer=False))
+ assert bucks.player_in_game(games, author) is not None
def test_info() -> None:
@@ -4213,3 +4272,286 @@ async def test_legend_info() -> None:
)
assert await brawl.legend_info(BrawlKey, "invalidname") is None
+
+
+def test_add_player_get_player_and_ready_to_start() -> None:
+ game = bucks.BlackjackGame(MockMember(), multiplayer=True)
+ owner = game.players[0]
+
+ assert game.multiplayer is True
+ assert len(game.players) == 1
+ assert game.ready_to_start() is True
+ assert int(owner.bet) == 10 # please move starting bet into a variable
+
+ # test get_player with players not in game
+ p2 = MockUser()
+ assert game.get_player(p2) is None
+
+ # add another player and ensure get_player finds them
+ game.add_player(p2)
+ assert len(game.players) == 2
+ found = game.get_player(p2)
+ assert found is not None
+ assert found.name == p2
+
+ # this should be tested when
+ # BlackjackPlayer.bet's type is changed to 'int | None'
+ # for more info grep for '805746791'
+ # found.bet = None
+ # assert game.ready_to_start() is False
+ # found.bet = 5
+ # assert game.ready_to_start() is True
+ # owner.bet = None
+ # assert game.ready_to_start() is False
+ # owner.bet = 5
+ # assert game.ready_to_start() is True
+
+
+def test_is_turn_and_advance_turn_skips_perfect_players() -> None:
+ game = bucks.BlackjackGame(MockMember(), multiplayer=True)
+ game.add_player(MockMember())
+ game.add_player(MockMember())
+
+ assert game.turn_idx == 0
+ assert game.is_turn(game.players[0]) is True
+
+ game.advance_turn()
+ assert game.turn_idx == 1
+ assert game.is_turn(game.players[1]) is True
+
+ assert len(game.players) == 3
+ # test skipping of players who blackjacked
+ game.players[2].hand = [
+ bucks.BlackjackGame.AceVal, bucks.BlackjackGame.FaceVal,
+ ]
+ game.advance_turn()
+ # make sure the turn_idx is no longer valid.
+ # all we know is that it should not a valid idx into BlackjackGame.players
+ assert not (game.turn_idx > 0 and game.turn_idx < len(game.players))
+
+
+def test_dealer_draw_stops_at_dealer_soft_goal() -> None:
+ game = bucks.BlackjackGame(MockMember(), multiplayer=True)
+
+ with pytest.MonkeyPatch.context() as mp:
+ mp.setattr("random.randint", lambda x, _: x)
+
+ # this is major ass please replace dealerUp & dealerSum with dealerCards
+ game.dealerUp = 10
+ game.dealerSum = bucks.BlackjackGame.DealerSoftGoal - 1
+ game.deck = [1, 5, 9, 11]
+ dealer_cards = game.dealer_draw()
+ assert dealer_cards == [10, 6, 1]
+ assert game.dealerSum == 17
+
+ # test no draw on soft-goal
+ game.dealerUp = 10
+ game.dealerSum = bucks.BlackjackGame.DealerSoftGoal
+ game.deck = [1, 5, 9, 11]
+ dealer_cards = game.dealer_draw()
+ assert dealer_cards == [10, 7]
+ assert game.dealerSum == bucks.BlackjackGame.DealerSoftGoal
+
+
+def make_blackjack_multiplayer_with_unique_user_id(
+ num_of_players: int,
+) -> bucks.BlackjackGame:
+ """
+ Create a multiplayer blackjack games with unique user-ids.
+
+ Args:
+ num_of_players (int): 1-9 inclusive. The number of players in the game.
+
+ Returns:
+ BlackjackGame: the created blackjack game
+
+ """
+ assert num_of_players > 0
+ assert num_of_players < 10
+ game = bucks.BlackjackGame(
+ MockMember(user=MockUser(user_id=1111)),
+ multiplayer=True,
+ )
+ for i in range(num_of_players - 1):
+ d = i + 2
+ new_id = d + d * 10 + d * 100 + d * 1000
+ game.add_player(MockMember(user=MockUser(user_id=new_id)))
+ return game
+
+
+def test_blackjack_multiplayer_start_game_skip_perfected_players() -> None:
+ with pytest.MonkeyPatch.context() as mp:
+ mp.setattr("random.randint", lambda x, _: x) # for deck draws
+
+ game = make_blackjack_multiplayer_with_unique_user_id(3)
+ game.deck = [
+ 1, 2, # no dealer blackjack
+ 3, 4, 5, 6, 7, 8,
+ ]
+ report = game.start_game()
+ assert game.is_turn(game.players[0])
+ assert report.endswith(f"<@1111> it is your turn! {bucks.GameHelpMsg}")
+
+ game = make_blackjack_multiplayer_with_unique_user_id(3)
+ game.deck = [
+ 1, 2, # no dealer blackjack
+ bucks.BlackjackGame.AceVal, bucks.BlackjackGame.FaceVal,
+ 3, 4, 5, 6, 7, 8,
+ ]
+ report = game.start_game()
+ assert game.is_turn(game.players[1])
+ assert report.endswith(f"<@2222> it is your turn! {bucks.GameHelpMsg}")
+
+ game = make_blackjack_multiplayer_with_unique_user_id(4)
+ game.deck = [
+ 1, 2, # no dealer blackjack
+ bucks.BlackjackGame.AceVal, bucks.BlackjackGame.FaceVal,
+ bucks.BlackjackGame.AceVal, bucks.BlackjackGame.FaceVal,
+ 3, 4, 5, 6,
+ ]
+ report = game.start_game()
+ assert game.is_turn(game.players[2])
+ assert report.endswith(f"<@3333> it is your turn! {bucks.GameHelpMsg}")
+
+
+def test_blackjack_multiplayer() -> None:
+ # this is sort of an integration test I guess
+ game = make_blackjack_multiplayer_with_unique_user_id(5)
+ p2 = game.players[1]
+ p3 = game.players[2]
+ p5 = game.players[4]
+ with pytest.MonkeyPatch.context() as mp:
+ mp.setattr("random.randint", lambda x, _: x) # for deck draws
+ mp.setattr(
+ "random.choice",
+ operator.itemgetter(0),
+ ) # for facecard names
+ game.deck = [
+ 1, 2, # no dealer blackjack
+ 3, 4, 10, 9, 7, 10,
+ bucks.BlackjackGame.AceVal, bucks.BlackjackGame.FaceVal,
+ bucks.BlackjackGame.AceVal, bucks.BlackjackGame.AceVal,
+ 10, 4,
+ ]
+ report = game.start_game() # will not blackjack
+ # maybe overkill?
+ assert game.dealerUp == 1
+ assert game.dealerSum == 3
+ assert not game.round_over()
+ assert report == """\
+The dealer is showing 1, with one card face down.
+<@1111> your starting hand consists of a 3 and a 4. Your total is 7.
+<@2222> your starting hand consists of a 10 and a 9. Your total is 19.
+<@3333> your starting hand consists of a 7 and a 10. Your total is 17.
+<@4444> your starting hand consists of an Ace and a 10. \
+You hit 21! You win! Your winnings have been added to your balance.
+<@5555> your starting hand consists of two Aces. \
+One of them will act as a 1. Your total is 12.
+
+<@1111> it is your turn! Type !hit to deal another card to yourself, \
+or !stay to stop at your current total.\
+"""
+ game.stay_current_player()
+ assert game.is_turn(p2)
+ assert not game.round_over()
+ game.stay_current_player()
+ assert game.is_turn(p3)
+ assert not game.round_over()
+ game.stay_current_player()
+ assert game.is_turn(p5)
+ assert not game.round_over()
+ report = game.stay_current_player()
+ assert game.round_over()
+ for p in game.players:
+ assert not game.is_turn(p)
+ assert report == """\
+<@5555> you stayed.
+Round ended, the dealer will now play
+The dealer's cards are a 1, a 2, a 10, a 4 for a total of 17.
+<@1111>, That's closer to 21 than your sum of 7. \
+You lose! Your losses have been deducted from your balance.
+<@2222>, you're closer to 21 with a sum of 19. \
+You win! Your winnings have been added to your balance
+<@3333>, That ties your sum of 17. Your bet has been returned, <@3333>.
+<@5555>, That's closer to 21 than your sum of 12. \
+You lose! Your losses have been deducted from your balance.
+
+Round ended!\
+"""
+
+
+def test_deal_current_player() -> None:
+ with pytest.MonkeyPatch.context() as mp:
+ mp.setattr("random.randint", lambda x, _: x) # for deck draws
+ mp.setattr(
+ "random.choice",
+ operator.itemgetter(0),
+ ) # for facecard names
+ game = make_blackjack_multiplayer_with_unique_user_id(3)
+ game.deck = [
+ 1, 2, # no dealer blackjack
+ 1, 5,
+ 3, 4, 5, 6,
+ 7, 10,
+ ]
+ game.start_game()
+ report = game.deal_current_player()
+ assert game.players[0].hand == [1, 5, 7]
+ assert report.startswith(
+ "<@1111> you were dealt a 7, bringing your total to 13",
+ )
+ report = game.deal_current_player()
+ assert game.players[0].hand == [1, 5, 7, 10]
+ assert report.startswith(
+ "<@1111> you were dealt a 10, bringing your total to 23",
+ )
+ assert report.endswith(
+ "You busted. Game over.\n<@2222>, it is your turn.\n",
+ )
+ assert not game.is_turn(game.players[0])
+ assert game.is_turn(game.players[1])
+
+ game = make_blackjack_multiplayer_with_unique_user_id(2)
+ game.deck = [
+ 1, 2, # no dealer blackjack
+ bucks.BlackjackGame.AceVal, 5, # ace overflow
+ 5, 6, 10,
+ ]
+ game.start_game()
+ assert game.players[0].hand == [bucks.BlackjackGame.AceVal, 5]
+ report = game.deal_current_player()
+ assert game.players[0].hand == [1, 5, 10]
+ assert (
+ "To avoid busting, your Ace will be treated as a 1. "
+ "Your new total is 16"
+ ) in report
+ assert not game.players[0].check_bust()
+ assert game.is_turn(game.players[0])
+
+
+def test_blackjack_multiplayer_dealer_blackjack() -> None:
+ game = make_blackjack_multiplayer_with_unique_user_id(3)
+ with pytest.MonkeyPatch.context() as mp:
+ mp.setattr("random.randint", lambda x, _: x) # for deck draws
+ mp.setattr(
+ "random.choice",
+ operator.itemgetter(0),
+ ) # for facecard names
+ game.deck = [
+ bucks.BlackjackGame.AceVal, bucks.BlackjackGame.FaceVal,
+ 3, 4,
+ bucks.BlackjackGame.AceVal, bucks.BlackjackGame.FaceVal,
+ 7, 10,
+ ]
+ report = game.start_game()
+ assert report == """\
+The dealer blackjacked!
+<@1111> your starting hand consists of 3 and 4. \
+You did not blackjack, you lose.
+<@2222> your starting hand consists of 11 and 10. \
+You tied with the dealer, your bet is returned.
+<@3333> your starting hand consists of 7 and 10. \
+You did not blackjack, you lose.
+
+Round ended.\
+"""
diff --git a/bucks.py b/bucks.py
index 0de79e9..54bec9b 100644
--- a/bucks.py
+++ b/bucks.py
@@ -29,6 +29,82 @@
" going, {}. Type !blackjack to start one."
)
+NoMultiplayerGameMsg = (
+ "You do not currently have a multiplayer game of blackjack"
+ " going, {}. Type '!blackjack new' to start one."
+)
+
+InvalidBetMsg = (
+ "Invalid bet. Please choose a number greater than or equal"
+ " to 0, or enter \"all\" to bet your whole balance, {}."
+)
+
+WinMsg = "You win! Your winnings have been added to your balance"
+LoseMsg = "You lose! Your losses have been deducted from your balance"
+
+GameHelpMsg = (
+ "Type !hit to deal another card to yourself, "
+ "or !stay to stop at your current total."
+)
+
+
+class BlackjackPlayer:
+ """
+ BlackjackPlayer instantce.
+
+ Attributes:
+ name (Nextcord.User | Nextcord.Member):
+ The discord user representing the player
+ hand (list[int]): The player's current hand
+ bet (int): The player's current bet
+
+ Methods:
+ check_bust(): Check if the player has gone over BlackjackGame.Goal.
+ perfect(): Check if the user has reached BlackjackGame.Goal
+
+ """
+
+ def __init__(self, name: nextcord.User | nextcord.Member) -> None:
+ """
+ Create a new BlackjackPlayer instance.
+
+ Args:
+ name (nextcord.User or Member):
+ The discord user representing this player
+
+ """
+ self.name: nextcord.User | nextcord.Member = name
+ self.hand: list[int] = []
+ # TODO: make BlackjackPlayer.bet's type be 'int | None'
+ # and add a phase after owner does '!tablestart' where
+ # people make their bets
+ # grep for '805746791' when this is changed
+ self.bet: int = 10
+
+ def check_bust(self) -> bool:
+ """
+ Check if the player has gone over BlackjackGame.Goal.
+
+ Returns:
+ bool: Whether the user has gone over BlackjackGame.Goal.
+
+ """
+ return sum(self.hand) > BlackjackGame.Goal
+
+ def perfect(self) -> bool:
+ """
+ Check if the user has reached Goal, and therefore gotten Blackjack.
+
+ In the actual game of Blackjack, getting Blackjack requires hitting
+ 21 with just your first two cards; for the sake of simplicity, use
+ this method for checking if the user has reached Goal at all.
+
+ Returns:
+ bool: Whether the user has gotten Blackjack.
+
+ """
+ return sum(self.hand) == BlackjackGame.Goal
+
class BlackjackGame:
"""
@@ -43,29 +119,43 @@ class BlackjackGame:
FaceVal (int): The value of a face card (J Q K)
Goal (int): The desired score
CardVals (tuple[int, ...]): Blackjack values for each card
- user (nextcord.User or Member): The user who is playing this game
- bet (int): The number of BeardlessBucks the user is betting
+ owner (nextcord.User or Member): The user who is owns this game
+ players (list[BlackjackPlayer]): The players in the game
+ turn_idx (int): an index into players that holds player to play
+ multiplayer (bool): Whether this match is multiplayer
dealerUp (int): The card the dealer is showing face-up
dealerSum (int): The running count of the dealer's cards
deck (list): The cards remaining in the deck
- hand (list): The list of cards the user has been dealt
+ started (bool): Whether the match/round started
message (str): The report to be sent in the Discord channel
Methods:
- check_bust():
- Checks if the user has gone over Goal
- deal_to_player():
- Deals the user a card
+ dealer_draw():
+ Draw the dealers cards (at the end of the game).
+ _end_round():
+ Ends a round after everyone plays their turn.
+ deal_to_current_player():
+ Deals the player whose turn it is a card.
+ card_name(card):
+ Gives the human-friendly name of a given card.
+ ready_to_start():
+ Checks if a multiplayer match is ready to start.
+ add_player(player):
+ Add a player to multiplayer blackjack match.
+ is_turn(player):
+ Checks whether it is the turn of a given player.
deal_top_card():
Removes the top card from the deck.
- perfect():
- Checks if the user has reached a Blackjack
- starting_hand():
- Deals the user a starting hand of 2 cards
- stay():
- Determines the game result after ending the game
- card_name(card):
- Gives the human-friendly name of a given card
+ _deal_cards():
+ Deal the starting cards to the dealer and all players.
+ _start_game_regular():
+ Starts a round where the dealer did not blackjack
+ _start_game_blackjack():
+ Starts a round where the dealer blackjacked.
+ _dealer_blackjack_end_round():
+ End a round where the dealer blackjacked.
+ start_game():
+ Deal the user(s) a starting hand of 2 cards.
"""
@@ -74,11 +164,13 @@ class BlackjackGame:
FaceVal = 10
Goal = 21
CardVals = (2, 3, 4, 5, 6, 7, 8, 9, 10, FaceVal, FaceVal, FaceVal, AceVal)
+ NumOfDecksInMatch = 4
def __init__(
self,
- user: nextcord.User | nextcord.Member,
- bet: int,
+ owner: nextcord.User | nextcord.Member,
+ *,
+ multiplayer: bool,
) -> None:
"""
Create a new BlackjackGame instance.
@@ -88,18 +180,135 @@ def __init__(
reaching DealerSoftGoal.
Args:
- user (nextcord.User or Member): The user who is playing this game
- bet (int): The number of BeardlessBucks the user is betting
+ owner (nextcord.User or Member): The user who is owning this game
+ in a singleplayer game the owner is also the only player.
+ in multiplayer the owner is the one who can start the round.
+ multiplayer (bool): Whether to make a multiplayer game
"""
- self.user = user
- self.bet = bet
+ self.owner = BlackjackPlayer(owner)
+ self.players: list[BlackjackPlayer] = [self.owner]
self.deck: list[int] = []
- self.deck.extend(BlackjackGame.CardVals * 4)
- self.hand: list[int] = []
- self.dealerUp = self.deal_top_card()
- self.dealerSum = self.dealerUp + self.deal_top_card()
- self.message = self.starting_hand()
+ self.deck.extend(BlackjackGame.CardVals * 4 * 4) # 4 decks
+ # TODO: dealerUp should NEVER be None
+ # and dealerSum should NEVER be 0
+ self.dealerUp: int | None = None
+ self.dealerSum: int = 0
+ self.started: bool = False
+ self.turn_idx = 0
+ self.multiplayer = multiplayer # only multiplayer games can be joined
+ if not multiplayer:
+ self.message = self.start_game()
+ else:
+ self.message = "Multiplayer Blackjack game created!\n"
+
+ def dealer_draw(self) -> list[int]:
+ """
+ Simulate the dealer drawing cards.
+
+ Will draw cards until dealer is above DealerSoftGoal
+ Takes into accounts Ace overflow.
+
+ Returns:
+ list[int]: the dealers final hand
+
+ """
+ assert self.dealerUp is not None
+ dealer_cards: list[int] = [
+ self.dealerUp, self.dealerSum - self.dealerUp,
+ ]
+ while True:
+ if self.dealerSum > BlackjackGame.DealerSoftGoal:
+ if BlackjackGame.AceVal in dealer_cards:
+ self.dealerSum -= 10
+ dealer_cards[dealer_cards.index(BlackjackGame.AceVal)] = 1
+ else:
+ return dealer_cards
+ elif self.dealerSum == BlackjackGame.DealerSoftGoal:
+ return dealer_cards
+ dealt = self.deal_top_card()
+ dealer_cards.append(dealt)
+ self.dealerSum += dealt
+
+ def _play_dealer_turn(self) -> str:
+ # dealer should only draw if there is at least 1 player that stayed
+ for p in self.players:
+ if not p.perfect() and not p.check_bust():
+ if self.multiplayer:
+ report = "Round ended, the dealer will now play\n"
+ else:
+ report = "The dealer will now play\n"
+ dealer_cards: list[int] = self.dealer_draw()
+ report += "The dealer's cards are {} ".format(
+ ", ".join(
+ BlackjackGame.card_name(card)
+ for card in dealer_cards),
+ )
+ report += f"for a total of {self.dealerSum}.\n"
+ return report
+ return ""
+
+ def _end_round(self) -> str:
+ """
+ End a round where the dealer blackjacked.
+
+ Will draw the dealers cards only if at least one player stayed.
+
+ Returns:
+ str: final report
+
+ """
+ assert self.dealerUp is not None
+ assert self.dealerSum != 0
+ report = self._play_dealer_turn()
+ for p in self.players:
+ if p.perfect() or p.check_bust():
+ # these have already been handled and reported
+ continue
+ report += f"{p.name.mention}, "
+ if sum(p.hand) > self.dealerSum and not p.check_bust():
+ report += f"you're closer to {BlackjackGame.Goal} "
+ report += (
+ f"with a sum of {sum(p.hand)}. {WinMsg}"
+ )
+ write_money(
+ p.name, p.bet, writing=True, adding=True,
+ )
+ elif sum(p.hand) == self.dealerSum:
+ report += (
+ f"That ties your sum of {sum(p.hand)}. "
+ f"Your bet has been returned, {p.name.mention}."
+ )
+ elif self.dealerSum > BlackjackGame.Goal:
+ report += (
+ f"You have a sum of {sum(p.hand)}. "
+ f"The dealer busts. {WinMsg}"
+ )
+ write_money(
+ p.name, p.bet, writing=True, adding=True,
+ )
+ else:
+ report += (
+ f"That's closer to {BlackjackGame.Goal} "
+ f"than your sum of {sum(p.hand)}. {LoseMsg}."
+ )
+ write_money(
+ p.name, -p.bet, writing=True, adding=True,
+ )
+ if not p.bet:
+ report += (
+ "Unfortunately, you bet nothing, so this was all pointless."
+ )
+ report += "\n" # trust me this is needed
+ if not self.multiplayer:
+ return report
+ self.started = False
+ self.dealerUp = None
+ self.dealerSum = 0
+ for p in self.players:
+ p.hand = []
+ report += "\nRound ended!"
+ return report
@staticmethod
def card_name(card: int) -> str:
@@ -114,6 +323,9 @@ def card_name(card: int) -> str:
"""
if card == BlackjackGame.FaceVal:
+ # TODO: this can cause us to draw more of a single facecard
+ # than would exist in the card pool in a real game.
+ # fixing this is not simple
return "a " + random.choice(
(str(BlackjackGame.FaceVal), "Jack", "Queen", "King"),
)
@@ -121,174 +333,294 @@ def card_name(card: int) -> str:
return "an Ace"
return "an 8" if card == 8 else ("a " + str(card)) # noqa: PLR2004
- def deal_top_card(self) -> int:
+ # NOTE: this is currently useless
+ # for more info grep for '805746791'
+ def ready_to_start(self) -> bool:
"""
- Remove and return the top card from the deck.
+ Check if a multiplayer match is ready to start.
Returns:
- int: The value of the top card of the deck.
+ bool: whether all players have placed a bet.
"""
- return self.deck.pop(random.randint(0, len(self.deck) - 1))
+ assert self.multiplayer
+ return all(player.bet is not None for player in self.players)
- def perfect(self) -> bool:
+ def add_player(self, player: nextcord.User | nextcord.Member) -> None:
"""
- Check if the user has reached Goal, and therefore gotten Blackjack.
+ Add a player to a multiplayer blackjack match.
- In the actual game of Blackjack, getting Blackjack requires hitting
- 21 with just your first two cards; for the sake of simplicity, use
- this method for checking if the user has reached Goal at all.
+ Args:
+ player (nextcord.User | nextcord.Member): the player to add.
+
+ """
+ assert self.multiplayer
+ self.players.append(BlackjackPlayer(player))
+
+ def is_turn(self, player: BlackjackPlayer) -> bool:
+ """
+ Check whether it is the turn of a given player.
+
+ Args:
+ player (nextcord.User | nextcord.Member): the player to check.
Returns:
- bool: Whether the user has gotten Blackjack.
+ bool: whether is it the turn of 'player'
"""
- return sum(self.hand) == BlackjackGame.Goal
+ if self.turn_idx < 0 or self.turn_idx >= len(self.players):
+ return False
+ return self.players[self.turn_idx] == player
- def starting_hand(self) -> str:
+ def deal_top_card(self) -> int:
"""
- Deal the user a starting hand of 2 cards.
+ Remove and return the top card from the deck.
Returns:
- str: The message to show the user.
+ int: The value of the top card of the deck.
"""
- self.hand.append(self.deal_top_card())
- self.hand.append(self.deal_top_card())
- message = (
- "Your starting hand consists of"
- f" {BlackjackGame.card_name(self.hand[0])}"
- f" and {BlackjackGame.card_name(self.hand[1])}."
- f" Your total is {sum(self.hand)}. "
- )
- if self.perfect():
- message += (
- f"You hit {BlackjackGame.Goal}! You win, {self.user.mention}!"
- )
- else:
+ return self.deck.pop(random.randint(0, len(self.deck) - 1))
+
+ def _deal_cards(self) -> None:
+ """Deal the starting cards to the dealer and all players."""
+ self.dealerUp = self.deal_top_card()
+ self.dealerSum = self.dealerUp + self.deal_top_card()
+ for p in self.players:
+ p.hand = []
+ p.hand.append(self.deal_top_card())
+ p.hand.append(self.deal_top_card())
+
+ def _dealer_blackjack_end_round(self) -> None:
+ """End a round where the dealer blackjacked."""
+ assert self.dealerSum == self.Goal
+ if self.multiplayer:
+ self.turn_idx = len(self.players)
+
+ def _start_game_blackjack(self) -> str:
+ """Play players' turns after the dealer draws blackjacks."""
+ message = "The dealer blackjacked!\n"
+ for p in self.players:
message += (
- f"The dealer is showing {self.dealerUp},"
- " with one card face down. "
+ f"{p.name.mention} your starting hand consists of "
+ f"{p.hand[0]} and {p.hand[1]}. "
)
- if self.check_bust():
- self.hand[1] = 1
- self.bet *= -1
- message = (
- "Your starting hand consists of two Aces."
- " One of them will act as a 1. Your total is 12. "
+ if p.perfect():
+ message += (
+ "You tied with the dealer, your bet is returned.\n"
)
- message += (
- "Type !hit to deal another card to yourself, or !stay"
- f" to stop at your current total, {self.user.mention}."
- )
+ else:
+ message += (
+ "You did not blackjack, you lose.\n"
+ )
+ write_money(p.name, -p.bet, writing=True, adding=True)
+ self._dealer_blackjack_end_round()
+ message += "\nRound ended."
return message
- def deal_to_player(self) -> str:
+ def _start_game_regular(self) -> str:
"""
- Deal the user a single card.
+ Start a round where the dealer did not blackjack.
+
+ Deals cards to all players.
+ Handles ace overflows and player blackjacks.
Returns:
- str: The message to show the user.
+ str: human readable report message.
"""
- dealt = self.deal_top_card()
- self.hand.append(dealt)
- self.message = (
- f"You were dealt {BlackjackGame.card_name(dealt)},"
- f" bringing your total to {sum(self.hand)}. "
+ message = (
+ f"The dealer is showing {self.dealerUp}, "
+ "with one card face down.\n"
)
- if BlackjackGame.AceVal in self.hand and self.check_bust():
- for i, card in enumerate(self.hand): # pragma: no branch
- if card == BlackjackGame.AceVal:
- self.hand[i] = 1
- self.bet *= -1
- break
- self.message += (
- "To avoid busting, your Ace will be treated as a 1."
- f" Your new total is {sum(self.hand)}. "
- )
- self.message += (
- "Your card values are {}. The dealer is"
- " showing {}, with one card face down."
- ).format(", ".join(str(card) for card in self.hand), self.dealerUp)
- if self.check_bust():
- self.message += f" You busted. Game over, {self.user.mention}."
- elif self.perfect():
- self.message += (
- f" You hit {BlackjackGame.Goal}! You win, {self.user.mention}!"
- )
- else:
- self.message += (
- " Type !hit to deal another card to yourself, or !stay"
- f" to stop at your current total, {self.user.mention}."
- )
- return self.message
+ append_help: bool = not self.multiplayer
+ for p in self.players:
+ if p.check_bust():
+ if self.multiplayer:
+ append_help = True
+ p.hand[1] = 1
+ message += (
+ f"{p.name.mention} your starting hand consists of two Aces."
+ " One of them will act as a 1. Your total is 12.\n"
+ )
+ else:
+ message += (
+ f"{p.name.mention} your starting hand consists of "
+ f"{BlackjackGame.card_name(p.hand[0])} "
+ f"and {BlackjackGame.card_name(p.hand[1])}. "
+ )
+ if p.perfect():
+ if not self.multiplayer:
+ append_help = False
+ elif p == self.players[self.turn_idx]:
+ self.advance_turn()
+ message += f"You hit {BlackjackGame.Goal}! {WinMsg}.\n"
+ write_money(p.name, p.bet, writing=True, adding=True)
+ else:
+ if self.multiplayer:
+ append_help = True
+ message += f"Your total is {sum(p.hand)}.\n"
+ if append_help:
+ if not self.multiplayer:
+ message += GameHelpMsg
+ else:
+ message += (
+ f"\n{self.players[self.turn_idx].name.mention} "
+ f"it is your turn! {GameHelpMsg}"
+ )
+ return message
- def check_bust(self) -> bool:
+ def start_game(self) -> str:
+ """
+ Deal the user(s) a starting hand of 2 cards.
+
+ Returns:
+ str: Human readable report.
+
+ """
+ self.turn_idx = 0
+ self.started = True
+ self._deal_cards()
+ if self.dealerSum == BlackjackGame.Goal:
+ return self._start_game_blackjack()
+ return self._start_game_regular()
+
+ def advance_turn(self) -> None:
"""
- Check if a user has gone over Goal.
+ End current player's turn.
- If so, invert their bet to facilitate subtracting it from their total.
+ Skips over all players that blackjacked.
+ """
+ while True:
+ self.turn_idx += 1
+ if self.turn_idx == len(self.players):
+ return
+ player = self.players[self.turn_idx]
+ # you can't bust without ever dealing
+ assert not player.check_bust()
+ # skip over all players that can't play
+ if not player.perfect():
+ return
+
+ def round_over(self) -> bool:
+ """
+ Check if the round ended.
Returns:
- bool: Whether the user has gone over Goal.
+ bool: if the round ended
"""
- if sum(self.hand) > BlackjackGame.Goal:
- self.bet *= -1
- return True
- return False
+ assert self.turn_idx <= len(self.players)
+ return self.turn_idx == len(self.players)
- def stay(self) -> int:
+ def deal_current_player(self) -> str:
"""
- End the game.
+ Deal the player whose turn it is a single card.
Returns:
- int: 1 if user's balance changed; else, 0.
-
- """
- change = 1
- while self.dealerSum < BlackjackGame.DealerSoftGoal:
- self.dealerSum += self.deal_top_card()
- self.message = "The dealer has a total of {}."
- if sum(self.hand) > self.dealerSum and not self.check_bust():
- self.message += f" You're closer to {BlackjackGame.Goal}"
- self.message += (
- " with a sum of {}. You win! Your winnings"
- " have been added to your balance, {}."
+ str: report
+
+ """
+ assert self.started
+ dealt = self.deal_top_card()
+ dealt_card = dealt
+ player = self.players[self.turn_idx]
+ player.hand.append(dealt)
+ new_hand = player.hand
+ append_help: bool = True
+ report = (
+ f"{player.name.mention} you were dealt "
+ f"{BlackjackGame.card_name(dealt_card)}, "
+ "bringing your total to "
+ )
+ if BlackjackGame.AceVal in player.hand and player.check_bust():
+ for i, card in enumerate(player.hand): # pragma: no branch
+ if card == BlackjackGame.AceVal:
+ player.hand[i] = 1
+ break
+ report += (
+ f"{sum(new_hand) + 10}. "
+ "To avoid busting, your Ace will be treated as a 1. "
+ f"Your new total is {sum(new_hand)}. "
)
- elif sum(self.hand) == self.dealerSum:
- change = 0
- self.message += (
- " That ties your sum of {}. Your bet has been returned, {}."
+ else:
+ report += (
+ f"{sum(new_hand)}. "
+ "Your card values are {}. The dealer is"
+ " showing {}, with one card face down."
+ ).format(", ".join(str(card) for card in new_hand), self.dealerUp)
+ if player.check_bust():
+ append_help = False
+ write_money(
+ player.name, -player.bet, writing=True, adding=True,
)
- elif self.dealerSum > BlackjackGame.Goal:
- self.message += (
- " You have a sum of {}. The dealer busts. You win!"
- " Your winnings have been added to your balance, {}."
+ self.advance_turn()
+ report += " You busted. Game over."
+ if not self.round_over():
+ report += (
+ f"\n{self.players[self.turn_idx].name.mention}, "
+ "it is your turn.\n"
+ )
+ elif player.perfect():
+ append_help = False
+ write_money(
+ player.name, player.bet, writing=True, adding=True,
)
- else:
- self.message += f" That's closer to {BlackjackGame.Goal}"
- self.message += (
- " than your sum of {}. You lose. Your loss"
- " has been deducted from your balance, {}."
+ report += (
+ f" You hit {BlackjackGame.Goal}! "
+ f"{WinMsg}, {player.name.mention}.\n"
)
- self.bet *= -1
- self.message = self.message.format(
- self.dealerSum, sum(self.hand), self.user.mention,
- )
- if not self.bet:
- self.message += (
- " Unfortunately, you bet nothing, so this was all pointless."
+ self.advance_turn()
+ if append_help:
+ report += f" {GameHelpMsg}"
+ elif self.round_over():
+ report += self._end_round()
+ return report
+
+ def stay_current_player(self) -> str:
+ """
+ Stay the current player.
+
+ if all other players' actions have been exhausted, end the round.
+
+ Returns:
+ bool: the round has ended.
+
+ """
+ report = f"{self.players[self.turn_idx].name.mention} you stayed.\n"
+ self.advance_turn()
+ if self.round_over():
+ report += self._end_round()
+ else:
+ report += (
+ f"{self.players[self.turn_idx].name.mention}, "
+ "it is not your turn.\n"
)
- return change
+ return report
+
+ def get_player(
+ self, player: nextcord.User | nextcord.Member,
+ ) -> BlackjackPlayer | None:
+ """
+ Get player by name if in current match.
+
+ Args:
+ player (nextcord.User or nextcord.Member): the player to query
+
+ Returns:
+ BlackjackPlayer: the player if in match or None.
+
+ """
+ for p in self.players:
+ if p.name is player:
+ return p
+ return None
class MoneyFlags(Enum):
"""Enum for additional readability in the writeMoney method."""
- NotEnoughBucks = -2
- CommaInUsername = -1
+ NotEnoughBucks = -1
BalanceUnchanged = 0
BalanceChanged = 1
Registered = 2
@@ -300,7 +632,7 @@ def write_money(
*,
writing: bool,
adding: bool,
-) -> tuple[MoneyFlags, str | int | None]:
+) -> tuple[MoneyFlags, int]:
"""
Check or modify a user's BeardlessBucks balance.
@@ -311,24 +643,21 @@ def write_money(
adding (bool): Whether to add to or overwrite member's balance
Returns:
- tuple[MoneyFlags, str | int | None]: A tuple containing:
+ tuple[MoneyFlags, int]: A tuple containing:
MoneyFlags: enum representing the result of calling the method
- str or int or None: an additional report, if necessary.
+ int: the current money in the user's bank after the operation
"""
- if "," in member.name:
- return MoneyFlags.CommaInUsername, CommaWarn.format(member.mention)
+ assert "," not in member.name
with Path("resources/money.csv").open("r", encoding="UTF-8") as csv_file:
for row in csv.reader(csv_file, delimiter=","):
if str(member.id) == row[0]: # found member
if isinstance(amount, str): # for people betting all
amount = -int(row[1]) if amount == "-all" else int(row[1])
- new_bank: str | int = str(
- int(row[1]) + amount if adding else amount,
- )
- if writing and row[1] != new_bank:
+ new_bank: int = int(row[1]) + amount if adding else amount
+ if writing and row[1] != str(new_bank):
if int(row[1]) + amount < 0:
- return MoneyFlags.NotEnoughBucks, None
+ return MoneyFlags.NotEnoughBucks, int(row[1])
new_line = ",".join((row[0], str(new_bank), str(member)))
result = MoneyFlags.BalanceChanged
else:
@@ -349,13 +678,7 @@ def write_money(
with Path("resources/money.csv").open("a", encoding="UTF-8") as f:
f.write(f"\r\n{member.id},300,{member}")
- return (
- MoneyFlags.Registered,
- (
- "Successfully registered. You have 300"
- f" BeardlessBucks, {member.mention}."
- ),
- )
+ return MoneyFlags.Registered, 300
def register(target: nextcord.User | nextcord.Member) -> nextcord.Embed:
@@ -370,9 +693,7 @@ def register(target: nextcord.User | nextcord.Member) -> nextcord.Embed:
"""
result, bonus = write_money(target, 300, writing=False, adding=False)
- report = bonus if result in {
- MoneyFlags.CommaInUsername, MoneyFlags.Registered,
- } else (
+ report = bonus if result == MoneyFlags.Registered else (
"You are already in the system! Hooray! You"
f" have {bonus} BeardlessBucks, {target.mention}."
)
@@ -413,13 +734,15 @@ def balance(
f"{bal_target.mention}'s balance is {bonus} BeardlessBucks."
)
else:
- report = str(bonus) if result in {
- MoneyFlags.CommaInUsername, MoneyFlags.Registered,
- } else "Error!"
+ report = (
+ NewUserMsg.format(msg.author.mention)
+ if result == MoneyFlags.Registered
+ else "Error!"
+ )
return bb_embed("BeardlessBucks Balance", report)
-def reset(target: nextcord.User | nextcord.Member) -> nextcord.Embed:
+def reset(target: nextcord.User | nextcord.Member) -> str:
"""
Reset a user's Beardless balance to 200.
@@ -427,15 +750,15 @@ def reset(target: nextcord.User | nextcord.Member) -> nextcord.Embed:
target (nextcord.User or Member): The user to reset
Returns:
- nextcord.Embed: the report of the target's balance reset.
+ str: the report of the target's balance reset.
"""
- result, bonus = write_money(target, 200, writing=True, adding=False)
- report = bonus if result in {
- MoneyFlags.CommaInUsername, MoneyFlags.Registered,
- } else f"You have been reset to 200 BeardlessBucks, {target.mention}."
- assert isinstance(report, str)
- return bb_embed("BeardlessBucks Reset", report)
+ result, _ = write_money(target, 200, writing=True, adding=False)
+ if result == MoneyFlags.Registered:
+ report = NewUserMsg.format(target.mention)
+ else:
+ report = f"You have been reset to 200 BeardlessBucks, {target.mention}."
+ return report
def leaderboard(
@@ -513,10 +836,8 @@ def flip(author: nextcord.User | nextcord.Member, bet: str | int) -> str:
"""
heads = random.randint(0, 1)
- report = (
- "Invalid bet. Please choose a number greater than or equal"
- " to 0, or enter \"all\" to bet your whole balance, {}."
- )
+ report = InvalidBetMsg
+ assert "," not in author.name
if bet == "all":
if not heads:
bet = "-all"
@@ -532,9 +853,6 @@ def flip(author: nextcord.User | nextcord.Member, bet: str | int) -> str:
result, bank = write_money(author, 300, writing=False, adding=False)
if result == MoneyFlags.Registered:
report = NewUserMsg
- elif result == MoneyFlags.CommaInUsername:
- assert isinstance(bank, str)
- report = bank
elif isinstance(bet, int) and isinstance(bank, int) and bet > bank:
report = (
"You do not have enough BeardlessBucks to bet that much, {}!"
@@ -543,77 +861,111 @@ def flip(author: nextcord.User | nextcord.Member, bet: str | int) -> str:
if isinstance(bet, int) and not heads:
bet *= -1
result = write_money(author, bet, writing=True, adding=True)[0]
- report = (
- "Heads! You win! Your winnings have"
- " been added to your balance, {}."
- ) if heads else (
- "Tails! You lose! Your losses have been"
- " deducted from your balance, {}."
- )
+ report = f"Heads! {WinMsg}" if heads else f"Tails! {LoseMsg}"
+ report += f", {author.mention}.\n"
if result == MoneyFlags.BalanceUnchanged:
report += (
- " Or, they would have been, if"
+ "Or, they would have been, if"
" you had actually bet anything."
)
return report.format(author.mention)
+def can_make_bet(
+ user: nextcord.User | nextcord.Member,
+ bet: str | int,
+) -> tuple[bool, str | None]:
+ if isinstance(bet, str):
+ if bet == "all":
+ return True, None
+ try:
+ bet_num: int = int(bet)
+ except ValueError:
+ return False, InvalidBetMsg.format(user.mention)
+ if bet_num < 0:
+ return False, InvalidBetMsg.format(user.mention)
+
+ result, bank = write_money(user, 300, writing=False, adding=False)
+ if result == MoneyFlags.Registered:
+ return True, NewUserMsg.format(user.name)
+ if isinstance(bank, int) and bet_num > bank:
+ return False, (
+ "You do not have enough BeardlessBucks to "
+ f"bet that much, {user.mention}!"
+ )
+
+ return True, None
+
+
+def make_bet(
+ author: nextcord.User | nextcord.Member,
+ game: BlackjackGame,
+ bet: str | int, # expected to be either "all" or a number
+) -> tuple[str, int]:
+ report = InvalidBetMsg
+ result, bank = write_money(author, 300, writing=False, adding=False)
+ if result == MoneyFlags.Registered:
+ report = NewUserMsg
+ elif isinstance(bet, int) and isinstance(bank, int):
+ if bet > bank:
+ report = (
+ "You do not have enough BeardlessBucks to bet that much, {}!"
+ )
+ else:
+ report = game.message
+ elif bet == "all":
+ assert bank is not None
+ bet = bank
+ report = game.message
+ return report, int(bet) # this cast should work
+
+
def blackjack(
- author: nextcord.User | nextcord.Member, bet: str | int,
+ author: nextcord.User | nextcord.Member,
+ bet: str | int | None,
) -> tuple[str, BlackjackGame | None]:
"""
Gamble a certain number of BeardlessBucks on blackjack.
Args:
author (nextcord.User or Member): The user who is gambling
- bet (str): The amount author is wagering
+ bet (str | int | None): The amount author is wagering.
+ if None then a multiplayer game is created & returned
Returns:
str: A report of the outcome and how author's balance changed.
BlackjackGame or None: If there is still a game to play,
returns the object representing the game of blackjack
- author is playing. Else, None.
+ author is playing. Else if game has ended in blackjack, None.
"""
game = None
- report = (
- "Invalid bet. Please choose a number greater than or equal"
- " to 0, or enter \"all\" to bet your whole balance, {}."
- )
- if bet != "all":
+ if bet is None:
+ # bet being None means user wants a multiplayer game
+ game = BlackjackGame(author, multiplayer=True)
+ report = game.message
+ return report.format(author.mention), game
+ if isinstance(bet, str) and bet != "all":
try:
bet = int(bet)
except ValueError:
- bet = -1
+ return InvalidBetMsg.format(author.mention), game
if (
(isinstance(bet, str) and bet == "all")
or (isinstance(bet, int) and bet >= 0)
):
- result, bank = write_money(author, 300, writing=False, adding=False)
- if result == MoneyFlags.Registered:
- report = NewUserMsg
- elif result == MoneyFlags.CommaInUsername:
- assert isinstance(bank, str)
- report = bank
- elif isinstance(bet, int) and isinstance(bank, int) and bet > bank:
- report = (
- "You do not have enough BeardlessBucks to bet that much, {}!"
- )
- else:
- if bet == "all":
- assert bank is not None
- bet = bank
- game = BlackjackGame(author, int(bet))
- report = game.message
- if game.perfect():
- write_money(author, bet, writing=True, adding=True)
- game = None
+ game = BlackjackGame(author, multiplayer=False)
+ report, bet = make_bet(author, game, bet)
+ player = game.players[0]
+ player.bet = bet
+ if player.perfect():
+ game = None
return report.format(author.mention), game
-def active_game(
+def player_in_game(
games: list[BlackjackGame], author: nextcord.User | nextcord.Member,
-) -> BlackjackGame | None:
+) -> tuple[BlackjackGame, BlackjackPlayer] | None:
"""
Check if a user has an active game of Blackjack.
@@ -624,9 +976,13 @@ def active_game(
author (nextcord.User or Member): The user who is gambling
Returns:
- BlackjackGame or None: The user's current Blackjack game,
- if one exists. Else, None.
+ tuple[BlackjackGame, BlackjackPlayer] or None: The player associated
+ with the discord account and the game they're in if they're in one.
+ Else, None.
"""
- game = [g for g in games if g.user == author]
- return game[0] if game else None
+ for game in games:
+ player = game.get_player(author)
+ if player is not None:
+ return game, player
+ return None
diff --git a/misc.py b/misc.py
index cbf09e5..b0b1f94 100644
--- a/misc.py
+++ b/misc.py
@@ -986,6 +986,28 @@ def get_last_numeric_char(duration: str) -> int:
return len(duration)
+# TODO: merge with process_mute_target
+async def process_command_target(
+ ctx: BotContext, target: str | None, bot: commands.Bot,
+) -> nextcord.Member | None:
+ if not target:
+ await ctx.send(f"Please specify a target, {ctx.author.mention}.")
+ return None
+ try:
+ command_target = await commands.MemberConverter().convert(ctx, target)
+ except commands.MemberNotFound:
+ await ctx.send(embed=bb_embed(
+ "Beardless Bot Join",
+ "Invalid target! Target must be a mention or user ID.",
+ ))
+ return None
+ if bot.user is not None and command_target.id == bot.user.id:
+ await ctx.send("I'm not in match, idiot.")
+ return None
+ return command_target
+
+
+# TODO: merge with process_command_target
async def process_mute_target(
ctx: BotContext, target: str | None, bot: commands.Bot,
) -> nextcord.Member | None:
diff --git a/resources/images/coverage.svg b/resources/images/coverage.svg
index 4f6c255..07780ac 100644
--- a/resources/images/coverage.svg
+++ b/resources/images/coverage.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/resources/images/docstr-coverage.svg b/resources/images/docstr-coverage.svg
index 935903b..2b143db 100644
--- a/resources/images/docstr-coverage.svg
+++ b/resources/images/docstr-coverage.svg
@@ -8,13 +8,13 @@
-
+ docstr-coveragedocstr-coverage
- 37%
- 37%
+ 40%
+ 40%
\ No newline at end of file
diff --git a/resources/images/tests.svg b/resources/images/tests.svg
index 450afde..299494c 100644
--- a/resources/images/tests.svg
+++ b/resources/images/tests.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/resources/money.csv b/resources/money.csv
index 27f91a4..84525cc 100644
--- a/resources/money.csv
+++ b/resources/money.csv
@@ -437,7 +437,7 @@
912317264060096534,200,DAnnyBoyTriple1#9858
759156148795867167,280,ily jk.#5743
776233398954885140,300,Lev's Quizbowl Bot#1390
-123456789,300,Test#0000
+123456789,420,testname#0000
579482715888287754,300,cotton#0076
723044829025796147,600,wolves#2046
293132125190750208,290,Kit#1998
@@ -577,4 +577,5 @@
1349066861706481694,0,munatic.
916318405915709440,300,yoshi1236007
880074658710442004,300,haiferwd
-612953019901935616,200,itami.it
\ No newline at end of file
+612953019901935616,200,itami.it
+1,300,not#0000
\ No newline at end of file