博主是一名三国杀玩家,玩张昌蒲时发现算数这个技能可以通过编程解决。于是基于python和tk界面做了个算数器,经过博主实测,效果还行。
界面布局如下:
使用方法大家应该一看就能看懂:例如本次严教算数共有6张牌:9 Q Q 4 8 3 4 ,那么
则需要依次点击数字按钮即可完成选牌,选好牌后按计算就能得出结果,并且很人性化的是博主同时把计算结果复制到了你的剪切板中(至于这样要干嘛你玩多了就知道)。
下一次严教时则需要清空上次的选择和结果,就可以重新选牌。
下面讲讲算法的逻辑大致是数量优先,就是尽可能地分出来更多的牌,就是让未使用的牌最少(最好为零)。因为张昌蒲这个技能也是要求你尽量把牌都分完,如果未使用的牌数量超过1,那张昌蒲这轮的手牌上限就减一。一些资深的三国杀玩家可能会偏向于拿关键的牌而忽略牌数,这样也有道理,本算数辅助器其实只是辅助你计算得出牌数最优的结果,在此基础上你可以稍加调整,以上面的第一组Q Q,第二组 9 4 8 3 为例,假设你觉得4是一张桃比较关键你想分到第一组,你就把Q和4+8替换就行了,变成第一组 Q 4 8 ,第二组Q 9 3 。意思就是在得出结果的基础上做调整,拿到你最想要的牌(要是你有这个时间和精力的话,并且我认为资深一点的三国杀玩家对这个调整会更加熟练)。
经过测试,牌数在18以下基本上计算都是秒出结果,这样其实已经能满足几乎所有牌局的需求,因为目前我还很少遇见张昌蒲一次严教能出这么多牌的。
而且我这个算法得出的结果逻辑也是根据牌的顺序来给出的,这样就方便了你分牌时的操作,如图,你可以看到第一组8 4 9 10 K Q Q 3基本上顺序都是跟选牌时的顺序顺延下来的,包括第二组也是一样的,所以你分牌的时候找牌会非常方便。并且我还将该程序固定在了屏幕最顶层,就是只能通过按最小化按钮或关闭按钮才能在屏幕消失,这样就是为了让你分牌找牌的时候可以一边点游戏,一边看辅助器。
以下是部分源码,若想获得完整源码请私聊我或联系作者
import tkinter as tk
from tkinter import ttk, messagebox
class CardButton:
def __init__(self, text, row, col, command):
self.text = text
self.row = row
self.col = col
self.command = command
self.button = None
def create(self, parent):
self.button = ttk.Button(parent, text=self.text, command=self.command)
self.button.grid(row=self.row, column=self.col, padx=5, pady=5)
class App:
def __init__(self, root):
self.root = root
self.root.title("张昌蒲算数技能助手")
# 设置窗口大小和位置
window_width = 600
window_height = 500
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
x = (screen_width - window_width) // 2
y = (screen_height - window_height) // 2
root.geometry(f"{window_width}x{window_height}+{x}+{y}")
# 设置窗口置顶
root.attributes('-topmost', True)
# 创建菜单栏
menubar = tk.Menu(root)
root.config(menu=menubar)
# 添加"与作者交流"菜单项
def show_contact():
contact_dialog = ContactDialog(self.root)
contact_dialog.show()
menubar.add_command(label="与作者交流", command=show_contact)
# 设置样式
style = ttk.Style()
style.configure('Card.TButton', padding=6, width=6, font=('Arial', 12))
style.configure('Action.TButton', padding=6, width=12, font=('Arial', 11))
style.configure('Calculate.TButton', padding=8, width=15, font=('Arial', 12, 'bold'))
# 当前选择的牌
self.cards = []
self.create_widgets()
def create_widgets(self):
# 创建主框架
main_frame = ttk.Frame(self.root, padding="10")
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# 创建标题
title_label = ttk.Label(main_frame, text="请选择牌", font=("Arial", 16, "bold"))
title_label.grid(row=0, column=0, columnspan=7, pady=10)
# 创建卡牌区域框架
cards_frame = ttk.Frame(main_frame)
cards_frame.grid(row=1, column=0, columnspan=7, pady=5)
# 创建数字牌按钮(A-10)
card_values = ["A"] + [str(i) for i in range(2, 11)]
for i in range(10):
button = ttk.Button(
cards_frame,
text=card_values[i],
style='Card.TButton',
command=lambda x=i+1: self.add_card(x)
)
button.grid(row=i // 5, column=i % 5, padx=4, pady=4)
# 创建字母牌按钮(J、Q、K)
letter_cards = [("J", 11), ("Q", 12), ("K", 13)]
for i, (letter, value) in enumerate(letter_cards):
button = ttk.Button(
cards_frame,
text=letter,
style='Card.TButton',
command=lambda x=value: self.add_card(x)
)
button.grid(row=2, column=i, padx=4, pady=4)
# 创建当前选择的牌的显示区域
self.cards_label = ttk.Label(main_frame, text="已选择的牌:", font=("Arial", 12))
self.cards_label.grid(row=2, column=0, columnspan=7, pady=10)
# 创建操作按钮框架
action_frame = ttk.Frame(main_frame)
action_frame.grid(row=3, column=0, columnspan=7, pady=5)
# 创建操作按钮
ttk.Button(
action_frame,
text="清空",
style='Action.TButton',
command=self.clear_cards
).grid(row=0, column=0, padx=8)
ttk.Button(
action_frame,
text="删除最后一张",
style='Action.TButton',
command=self.remove_last_card
).grid(row=0, column=1, padx=8)
ttk.Button(
action_frame,
text="计算",
style='Calculate.TButton',
command=self.calculate
).grid(row=0, column=2, padx=8)
# 创建结果显示区域
self.result_text = tk.Text(main_frame, height=7, width=50, font=("Arial", 11))
self.result_text.grid(row=4, column=0, columnspan=7, pady=10)
self.result_text.config(state=tk.DISABLED)
def add_card(self, value):
self.cards.append(value)
self.update_cards_display()
def clear_cards(self):
self.cards = []
self.update_cards_display()
self.clear_result()
def remove_last_card(self):
if self.cards:
self.cards.pop()
self.update_cards_display()
self.clear_result()
def update_cards_display(self):
# 将数字转换为显示文本
display_cards = []
for card in self.cards:
if card == 1:
display_cards.append("A")
elif card == 11:
display_cards.append("J")
elif card == 12:
display_cards.append("Q")
elif card == 13:
display_cards.append("K")
else:
display_cards.append(str(card))
self.cards_label.config(text="已选择的牌:" + " ".join(display_cards))
def clear_result(self):
self.result_text.config(state=tk.NORMAL)
self.result_text.delete(1.0, tk.END)
self.result_text.config(state=tk.DISABLED)
def number_to_card(self, number):
if number == 1:
return "A"
elif number == 11:
return "J"
elif number == 12:
return "Q"
elif number == 13:
return "K"
return str(number)
def format_group(self, group):
return [self.number_to_card(num) for num in group]
def calculate(self):
if len(self.cards) < 4:
messagebox.showerror("错误", "至少需要4张牌!")
return
result, num_used, unused = find_equal_sum_groups(self.cards)
self.result_text.config(state=tk.NORMAL)
self.result_text.delete(1.0, tk.END)
if result:
group1, group2 = result
self.result_text.insert(tk.END, "找到解决方案!\n")
self.result_text.insert(tk.END, f"使用了 {num_used} 张牌\n")
formatted_group1 = self.format_group(group1)
formatted_group2 = self.format_group(group2)
self.result_text.insert(tk.END, f"第一组: {formatted_group1} (和为 {sum(group1)})\n")
self.result_text.insert(tk.END, f"第二组: {formatted_group2} (和为 {sum(group2)})\n")
if unused:
formatted_unused = self.format_group(unused)
self.result_text.insert(tk.END, f"未使用的牌: {formatted_unused}\n")
# 自动复制结果到剪贴板
simplified_result = f"第一组:{' '.join(formatted_group1)};第二组:{' '.join(formatted_group2)}"
self.root.clipboard_clear()
self.root.clipboard_append(simplified_result)
self.result_text.insert(tk.END, "\n结果已复制到粘贴板中")
else:
self.result_text.insert(tk.END, "没有找到可行的分组方案。\n")
self.result_text.config(state=tk.DISABLED)
最后祝大家使用顺利,牌技日益涨进,有疑问可留言或私聊我