You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python图形化Blackjack游戏:实现游戏循环重置的技术问询

解决Blackjack游戏New Game按钮无法重置的问题

我看了你的Blackjack代码,问题主要出在没有统一管理游戏状态和重置逻辑,而且New Game按钮的点击判断完全没生效。咱们一步步来解决这个问题:

核心问题分析

  • 元素无统一管理:你直接绘制手牌文本、总分文本,但没保存引用,重置时无法批量清除旧内容
  • New Game按钮逻辑错误:代码里判断的是第一次点击的s变量,而非主循环里的clickPoint,永远触发不了重置
  • card_value()函数bug:直接用全局total判断Ace价值,后续抽牌时计算错误,需传入当前手牌总分作为参数
  • 缺少重置函数:初始化逻辑线性执行,无法回到初始状态

修复后的完整代码

import graphics as gr
import random

win = gr.GraphWin("Blackjack", 700, 700)
win.setCoords(0.0, 0.0, 61.0, 80.0)

# 初始化固定UI元素(不需要重置的部分)
def init_static_ui():
    # Game Title
    ptitle = gr.Point(30, 77)
    title = gr.Text(ptitle, "Black Jack")
    title.draw(win)
    # Player/Dealer Titles
    playerp = gr.Point(10, 70)
    player_title = gr.Text(playerp, "Player")
    player_title.draw(win)
    dealerp = gr.Point(50, 70)
    dealer_title = gr.Text(dealerp, "Dealer")
    dealer_title.draw(win)
    # Money Display
    money_sign = gr.Text(gr.Point(27,70), "$").draw(win)
    global player_money, money_point
    player_money = 100
    money_point = gr.Text(gr.Point(30, 70), player_money)
    money_point.draw(win)
    # Card Slots (玩家和庄家的牌框)
    global player_slots, dealer_slots, player_text_points, dealer_text_points
    player_slots = []
    for x in range(1, 20, 10):
        for y in range(20, 68, 8):
            rect = gr.Rectangle(gr.Point(x, y), gr.Point(x + 10, y + 8))
            rect.draw(win)
            player_slots.append(rect)
    dealer_slots = []
    for x in range(40, 60, 10):
        for y in range(20, 68, 8):
            rect = gr.Rectangle(gr.Point(x, y), gr.Point(x + 10, y + 8))
            rect.draw(win)
            dealer_slots.append(rect)
    # 文本位置标记
    player_text_points = []
    for x in range(6, 20, 10):
        for y in range(23, 68, 8):
            player_text_points.append(gr.Point(x, y))
    dealer_text_points = []
    for x in range(45, 60, 10):
        for y in range(23, 68, 8):
            dealer_text_points.append(gr.Point(x, y))
    # 按钮
    global hitbox, standB, newbox
    hitbox = gr.Rectangle(gr.Point(25, 55), gr.Point(35, 65))
    hitbox.draw(win)
    hitButton = gr.Text(gr.Point(30, 60), "Hit")
    hitButton.draw(win)
    standB = gr.Rectangle(gr.Point(25, 40), gr.Point(35, 50))
    standB.draw(win)
    standTextp = gr.Text(gr.Point(30, 45), "Stand")
    standTextp.draw(win)
    newbox = gr.Rectangle(gr.Point(25.5, 5), gr.Point(35.5, 15))
    newbox.draw(win)
    newButton = gr.Text(gr.Point(30.5, 10), "New Game")
    newButton.draw(win)
    # 总分标签(固定文本)
    gr.Text(gr.Point(10, 10), "Player Hand Total:").draw(win)
    gr.Text(gr.Point(50, 10), "Dealer Hand Total:").draw(win)

# 生成牌堆
def card_deck():
    card_value = ['Ace', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
    card_type = ['Hearts', 'Spades', 'Clubs', 'Diamonds']
    deck = []
    for i in card_type:
        for j in card_value:
            deck.append(j + ' of ' + i)
    return deck

# 计算单张牌的价值(修正:传入当前总分判断Ace的价值)
def card_value(card, current_total):
    card_prefix = card.split()[0]
    if card_prefix in ('J', 'Q', 'K', '10'):
        return 10
    elif card_prefix in ('2', '3', '4', '5', '6', '7', '8', '9'):
        return int(card_prefix)
    elif card_prefix == 'Ace':
        return 11 if current_total <= 10 else 1

# 重置游戏状态
def reset_game():
    global game_elements, player_hand, dealer_hand, player_total, dealer_total, handcount, dealercount
    # 清除所有需要重置的元素
    for elem in game_elements:
        elem.undraw()
    game_elements = []
    
    # 初始化手牌和计数
    player_hand = []
    dealer_hand = []
    player_total = 0
    dealer_total = 0
    handcount = 0
    dealercount = 0
    
    # 发玩家初始两张牌
    for _ in range(2):
        card = random.choice(card_deck())
        player_hand.append(card)
        card_text = gr.Text(player_text_points[handcount], card)
        card_text.setSize(10)
        card_text.draw(win)
        game_elements.append(card_text)
        player_total += card_value(card, player_total)
        handcount += 1
    
    # 发庄家初始两张牌
    for _ in range(2):
        card = random.choice(card_deck())
        dealer_hand.append(card)
        card_text = gr.Text(dealer_text_points[dealercount], card)
        card_text.setSize(10)
        card_text.draw(win)
        game_elements.append(card_text)
        dealer_total += card_value(card, dealer_total)
        dealercount += 1
    
    # 绘制总分
    global player_total_text, dealer_total_text
    player_total_text = gr.Text(gr.Point(10, 6), player_total)
    player_total_text.draw(win)
    game_elements.append(player_total_text)
    dealer_total_text = gr.Text(gr.Point(50, 6), dealer_total)
    dealer_total_text.draw(win)
    game_elements.append(dealer_total_text)

# 判断点击是否在按钮内
def inside(point, rectangle):
    ll = rectangle.getP1()
    ur = rectangle.getP2()
    return ll.getX() < point.getX() < ur.getX() and ll.getY() < point.getY() < ur.getY()

# 初始化固定UI和游戏状态
init_static_ui()
game_elements = []
reset_game()

# 主游戏循环
while True:
    clickPoint = win.getMouse()
    if clickPoint is None:
        break
    
    # 处理Hit按钮
    if inside(clickPoint, hitbox):
        # 清除旧总分
        player_total_text.undraw()
        game_elements.remove(player_total_text)
        
        # 抽新牌
        card = random.choice(card_deck())
        player_hand.append(card)
        card_text = gr.Text(player_text_points[handcount], card)
        card_text.setSize(10)
        card_text.draw(win)
        game_elements.append(card_text)
        
        # 更新总分
        player_total += card_value(card, player_total)
        player_total_text = gr.Text(gr.Point(10, 6), player_total)
        player_total_text.draw(win)
        game_elements.append(player_total_text)
        handcount += 1
        
        # 判断是否爆牌
        if player_total > 21:
            bust_text = gr.Text(gr.Point(30, 20), "Player Busts!")
            bust_text.draw(win)
            game_elements.append(bust_text)
            # 更新金钱
            global player_money, money_point
            money_point.undraw()
            player_money -= 10
            money_point = gr.Text(gr.Point(30, 70), player_money)
            money_point.draw(win)
    
    # 处理Stand按钮(这里可以补充庄家抽牌逻辑)
    elif inside(clickPoint, standB):
        # 你可以在这里添加庄家自动抽牌的逻辑,然后判断胜负
        pass
    
    # 处理New Game按钮
    elif inside(clickPoint, newbox):
        reset_game()

关键改动说明

  1. 拆分UI为固定和可变部分:init_static_ui()负责绘制标题、按钮、牌框等不需要重置的元素,避免重复绘制。
  2. 游戏元素统一管理:用game_elements列表存储所有需要重置的文本元素,重置时批量清除。
  3. reset_game()函数:负责清除旧元素、重新初始化手牌、总分和计数,重新发初始牌。
  4. 修正card_value():传入当前手牌总分作为参数,正确判断Ace的价值。
  5. 修复New Game按钮逻辑:在主循环里检测点击是否在New Game按钮区域,调用reset_game()。

内容的提问来源于stack exchange,提问作者K. Mo3r

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:42:23