面试问题:一款双人纸牌游戏

2025-06-07

面试问题:一款双人纸牌游戏

我在interviewing.io上邀请面试者编写一个双人卡牌游戏的模拟程序。这是一个引人入胜的问题,能够揭示很多关于他们能力的信息。它不涉及任何技巧,也不涉及任何随机算法知识。它适合任何技能水平的程序员,并且支持所有常用语言。

我想分享这个问题以及我的一些见解。

双人纸牌游戏

我在提出这个问题时,会先强调代码的设计和结构。我不需要一个可以运行的程序,但需要知道它的入口点以及如何使用。

以下是游戏模拟的描述。

  • 这是一个双人纸牌游戏
  • 游戏从一副牌开始
  • 牌发给两位玩家
  • 每回合:
    • 两位玩家都翻开自己最上面的牌
    • 持有较高牌值的玩家拿走这些牌并将其放入自己的得分堆(每张牌得 1 分)
  • 这种情况持续到玩家没有剩余的牌
  • 得分最高的玩家获胜

它被认为是一种模拟,因为玩家没有任何选择。我们不需要担心输入。

此描述中存在一些未解答的问题,或者可能存在一些含糊之处。其中一些并非有意为之,但效果良好。候选人必须识别并解决需求中的问题。

根据这个描述,我希望人们能够编写模拟游戏的代码:写出谁在一轮游戏中获胜。

设计

受访者提问是好事,但不一定能揭示问题。我希望他们至少能得到一些反馈,表明他们理解了问题。除此之外,我比较灵活,因为我见过几种不同的解决问题的方式。我唯一看到的确定的模式是过度设计的负面模式:试图把所有事情都计划好,并写大量的笔记。如果人们在这里花的时间太长,我会鼓励他们开始写代码。

这个问题的复杂性不需要太多的前期工作。除了想出几个类,比如 和PlayerGame或者Card,你就可以开始写代码了。最好的方法是用实际代码来概括结构,许多表现优秀的考生都这么做。代码通常是最好的伪代码:在规划时,只需省略细节并走捷径即可。

卡片

一些候选人会从底层开始:深入研究卡片的定义,或者创建一个Deck类。我会问他们为什么要创建这些类,暗示这不是一个好方法。我鼓励他们从更高的层次思考,想想他们真正需要什么。

尤其是在时间紧迫的情况下,最好自上而下地思考,忽略细节,直到需要它们为止。也就是说,不要设计理论Deck类的所有方面,因为它们可能不会用到。可以勾勒出类的草图,放置一些变量,但细节应该简洁。

此时我会避免给出过多的指导;能够用代码构建问题是编程的一项基本技能。只有当我觉得问题严重,而且这种方法无法让他们完成任务时,我才会提出意见。如果他们感到自信并且正在取得进展,我会尽量不强迫他们接受理想化的观点,而是让他们顺其自然。

一个关键问题应该在设计过程中提出,最迟在比较牌时也应该提出,那就是我们处理的是什么类型的牌。并非所有人都熟悉标准扑克牌,这很正常;他们会问这些牌是什么类型的。其他人则假设是同花牌,并询问如何比较花色。在所有情况下,我都将需求简化为一组从 1 到 N 的带编号的牌。对于这个需求,Card完全可以不使用类,而只使用整数。不过,使用简单的类型也没什么问题Card

无论选择整数还是类,我都必须理解为什么做出这个决定。我尤其希望候选人能够告诉我他们为什么选择其中一个。代码也应该清晰易懂。像 这样的变量int[] array没什么用(是的,这种情况发生过一次)。用它们的逻辑值来命名,例如deck

游戏进度

我提到过编程应该自上而下。在完成初始设计和一些代码草图之后,我希望能够编写出主流程。我希望看到牌的发牌、回合的进行以及最终胜出的宣告。

先从粗略的结构开始,然后再填充细节,这很好。目前为止,我的经验是,不从宏观层面开始的人很难完成这个问题。

defn play_game = -> {
    deal_cards()

    take_turns()

    declare_winner()
}
Enter fullscreen mode Exit fullscreen mode

考虑到问题的简单性,如果之后可以重构为函数,也可以直接在函数中编写这些代码。由面试官主动发起重构,还是我提示,似乎反映了他们的经验水平。

自上而下设计的重要性在测试驱动开发和 YAGNI“你不会需要它”的概念等过程中得到了呼应。

常见问题

接下来就是填写细节了。这个问题有很多细节需要注意,但都不是太棘手,这对于面试来说还算不错。

玩家类

一个常见的错误是无法识别Player类型。我们最终会得到这样的编码模式:

vector<int> player1_cards, player2_cards;
int player1_score, player2_score;
Enter fullscreen mode Exit fullscreen mode

一系列匹配的变量名,例如player1_*player2_*,表明你应该有一个Player类型。大多数候选人都是自己得出这个结论的,要么Player直接从一个类开始,要么重构它。其他人需要一些提示,但通常很快就能理解。少数人只是没理解,需要明确的指导。

提示别人而不直接给出答案是很困难的。在这种情况下,我喜欢尝试这样一些方法:“看看从玩家1和玩家2开始的那些变量。你发现有什么规律了吗?”,“有什么结构可以用来抽象这些变量吗?”,或者“你能不能用更好的方法把它们分组?”

参数和成员变量

应该有一个Game类来管理模拟。类名并不重要,但它应该包含玩家以及游戏每个阶段的函数。这里考察的是应聘者的面向对象编程能力。

成员变量取代了函数重复传递参数的需要。所有函数都应该将玩家、分数和卡牌作为参数传递,但玩家应该作为实例的一部分。有些人会立即这样做,而另一些人则需要一些提示。

玩家详细信息几乎总是作为成员变量最好,但是在如何处理初始牌组和发牌方面存在一些变化。

再次强调,恰当的提示并非易事。我会尝试问一些问题,例如“是否有必要将玩家传递给所有函数?”“是否可以避免这种冗余?”,或者更直接地说:“这些玩家感觉就像属于这个类。” 说实话,我不记得我具体说了什么,但关键在于从概括性、甚至神秘性的角度入手。我需要说多少,以及需要说得多明确,很大程度上反映了受访者的技能。

神奇的数字和细节

代码中涉及的内容不多,但我不喜欢看到它们。例如,一个从 运行1..52到创建卡片的循环使用了一个魔法数字。在添加到分数时也会2出现一个:这有点微妙,但与我计划的扩展问题相关。

这看起来可能只是个小细节,但我注意到优秀的程序员对这类事情很敏感。他们会创建一个常量,给构造函数传入一个参数(可能带有默认值),或者干脆说:“是啊,这不太好,我应该之后再清理一下。”

到这个时候,我通常就能大致了解对方对编程语言的掌握程度了。与自然语言的比较似乎很公平,有些人说话流利,代码流畅、连贯。而有些人说话结结巴巴、忘词忘句、脑子里转述,代码也不太流畅。

虽然高级语法的考生表现不错,但也有人只用基本的命令式语法完成了题目。然而,真正的困难是不允许的:考生可以选择语言,所以对基础知识的期望是绝对的。

扩展问题

如果受访者已经解决了基本问题,并且我们至少还剩下 10 分钟,我会添加下一个要求:

  • 扩展游戏以支持两个以上的玩家

这需要多少修改取决于他们最初采用的方法。如果他们没有提出一个Player类,我会提出要求。如果代码比较稀疏,我也会要求重构成函数。代码需要处于良好的状态。否则,这个新要求就太令人望而生畏了。

粗略估计,大约有一半的人会问到这个问题。这使得它成为一个合适的筛选标准:我倾向于不让那些无法解决这个扩展问题的人通过。我会包容应届毕业生,但不会包容有行业经验的人。如果我估计他们无法完成这个问题,我通常不会问这个问题,或者有时我建议他们只扩展一个功能。我宁愿提前结束面试,也不愿增加额外的压力。

更多问题

改为 N 名玩家带来了一些新问题:

  • 发牌不再能用 if-else 语句来选择玩家了。我有点惊讶,竟然有这么多人不知道如何%在循环中使用模数从数组中选择元素。
  • 从列表中选择最大的牌不再是一个 if-else 问题。这对某些人来说是一个挑战,因为他们知道如何获取最大值,但并不总是知道那张牌的索引。
  • 判断游戏是否结束的循环现在变得非常重要,尤其是在玩家持有的牌数可能不一致的情况下。我注意到,优秀的程序员会自然而然地创建一个辅助函数,或者避免在循环中出现复杂的条件。

程序员对语言的了解似乎在这里起着至关重要的作用。熟悉自己语言的人更容易进行这些更改。对于熟悉 Python 的候选人来说尤其如此,因为 Python 有很棒的结构可以简化这一过程。

完成这个问题可以肯定地表明面试者确实是一名优秀的程序员,或者至少是一名优秀的程序员。我甚至不在乎他们在重构过程中是否遇到了小问题,但通常情况下,他们不会遇到任何问题。这是一个奇怪的规律,能够走到这一步的人往往不会在这个问题上遇到困难。

超级奖励问题

如果受访者成功添加了 N 位玩家的支持,我还会再做一点修改。这比之前的要求更具挑战性:“不要假设卡牌具有唯一的价值”。具体如下:

  • 消除卡片是唯一的假设(即采用标准卡组)
  • 如果玩家拥有相同点数的牌,则抽一张额外的牌,重复此过程,直到其中一人拥有更高的牌
  • 牌值最高的人拿走所有牌,每人得一分
  • 只有打平的玩家才能继续抽牌(1 名或以上玩家可以不参加额外抽牌)

只有一个人做到了这一点,为这个面试问题设定了黄金标准。

只有时间压力

我喜欢这个问题的一点是它没有任何花招或随机的算法知识。节奏安排似乎也很好:面试官能够应对不同程度的问题。考虑到我见过的面试官遇到的困难,这道题似乎能很好地测试各方面的能力。

作为面试练习,试着用代码来回答这个问题。注意哪些部分让你感到困难,并观察你花了多长时间。

文章来源:https://dev.to/mortoray/interview-question-a-two-player-card-game-67i
PREV
编程面试准备
NEXT
我不知道如何创建网站