Python实现Mastermind游戏:列表对比问题及索引异常求助
Mastermind桌游列表对比模块问题解决
问题说明
开发Mastermind桌游时,列表对比模块存在以下问题:
- 测试用例
list_1 = ["orange", "orange", "green", "red"]与list_2 = ["orange", "red", "orange", "green"],预期结果为1个hit(精确匹配)和3个almost(颜色匹配位置错误),实际得到2个hit和2个almost; - 参考的第三方Mastermind代码逻辑与自身需求不符;
- 自行编写的代码多数场景可用,但偶尔触发
IndexError: pop index out of range错误。
用户提供的三段相关代码如下:
第一段代码(首次尝试的对比逻辑)
if gs.board[actual_line] == gs.ai_choice[0]: print("You WIN") else: hit = [i for i, j in zip(gs.board[actual_line], gs.ai_choice[0]) if i == j] hit = len(hit) re_mylist = [i for i, j in zip(gs.board[actual_line], gs.ai_choice[0]) if i != j] re_ailist = [i for i, j in zip(gs.ai_choice[0], gs.board[actual_line]) if i != j] for idx, x in enumerate(re_mylist): for idy, y in enumerate(re_ailist): if x == y: common.append((idy, y)) print(f"hit: {hit}") white = len(set(common)) print(f"white: {white}")
第二段代码(参考的第三方代码)
import random import collections length = 4 # pattern = [random.choice('abcdef') for _ in range(length)] pattern = ['a', 'b', 'a', 'c'] print(*pattern) counted = collections.Counter(pattern) def running(): guess = input('?: ') guess_count = collections.Counter(guess) close = sum(min(counted[k], guess_count[k]) for k in counted) exact = sum(a == b for a, b in zip(pattern, guess)) close -= exact print('Exact: {}. Close: {}.'.format(exact, close)) return exact != length while running(): pass print('done!')
第三段代码(自行编写的代码)
import random from collections import Counter choices = ["red", "green", "yellow", "blue", "orange", "purple"] my_list = random.choices(choices, k=4) ai_list = random.choices(choices, k=4) def common_elements(list1, list2): result = [] for element in list1: if element in list2: result.append(element) return len(result) dummy_my_list = [x for x in my_list] dummy_ai_list = [x for x in ai_list] r = 0 my_pop = [] for i in range(4): if my_list[i] == ai_list[i]: my_pop.append(int(i)) r += 1 ac=Counter(dummy_my_list) bc=Counter(ai_list) res=[] pop = len(my_pop) if pop > 0: for i in range(pop): dummy_my_list.pop(my_pop[i]) dummy_ai_list.pop(my_pop[i]) for i in set(dummy_my_list).intersection(set(dummy_ai_list)): res.extend([i] * min(bc[i], ac[i])) w = len(res) print(f"hit: {r}") print(f"almost: {w}")
问题分析
第一段代码的结果错误问题:
- 过滤生成
re_mylist和re_ailist时,仅保留位置不匹配的元素,但未考虑重复颜色的计数逻辑; - 双重循环匹配后用集合去重,会错误统计匹配数量,导致almost值计算偏差。
- 过滤生成
第三段代码的索引越界问题:
my_pop存储的是原列表的索引,执行pop操作后列表长度缩短,后续索引会失效(例如:原索引2的元素在pop(0)后变为索引1,再pop(2)就会超出范围);- 正向遍历索引执行pop是触发错误的直接原因。
解决方案
最优实现方案(基于Counter的简洁逻辑)
核心思路:先计算精确匹配数,再通过颜色计数统计总匹配数,总匹配数减去精确匹配数即为almost数。该方法无索引操作,避免越界问题,且能正确处理重复颜色场景。
from collections import Counter def calculate_hits_and_almosts(guess, secret): # 计算精确匹配的hit数量 hits = sum(g == s for g, s in zip(guess, secret)) # 统计两个列表的颜色出现次数 guess_counter = Counter(guess) secret_counter = Counter(secret) # 计算所有颜色的最小匹配数之和(包含hit) total_matches = sum(min(guess_counter[color], secret_counter[color]) for color in guess_counter) # almost数量 = 总匹配数 - 精确匹配数 almosts = total_matches - hits return hits, almosts # 验证用户测试用例 list_1 = ["orange", "orange", "green", "red"] list_2 = ["orange", "red", "orange", "green"] hits, almosts = calculate_hits_and_almosts(list_1, list_2) print(f"hit: {hits}") # 输出:1 print(f"almost: {almosts}") # 输出:3
第三段代码的修复方案(若坚持保留原逻辑)
将索引pop的顺序改为从后往前,避免删除元素后索引偏移:
# 替换原pop循环代码 if pop > 0: # 从后往前删除索引,避免索引偏移 for idx in reversed(my_pop): dummy_my_list.pop(idx) dummy_ai_list.pop(idx)
内容的提问来源于stack exchange,提问作者Máté Bicsérdi
相关产品推荐
相关产品推荐

