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

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}")

问题分析

  1. 第一段代码的结果错误问题:

    • 过滤生成re_mylist和re_ailist时,仅保留位置不匹配的元素,但未考虑重复颜色的计数逻辑;
    • 双重循环匹配后用集合去重,会错误统计匹配数量,导致almost值计算偏差。
  2. 第三段代码的索引越界问题:

    • 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 10:03:33