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

Haskell按4元素迭代列表:实现winnedCards函数技术问询

嘿,我来帮你搞定这个Haskell卡牌游戏的函数实现!先理清楚需求和规则,然后一步步写代码~

实现思路与代码

首先,咱们先明确游戏规则的合理假设(如果和你的实际规则有出入,调整起来也很简单):

  • 两队对战,每轮出4张牌:先手队出2张,对方队出2张,出牌顺序是「先手→对方→先手→对方」
  • 胜负判定:
    1. 优先看压制花色(trump suit):如果本轮有压制花色的牌,点数最大的那张牌所属的队伍赢下本轮所有4张牌
    2. 如果没有压制花色,就看本轮第一张牌的花色(先手队的首张牌),该花色中点数最大的牌所属队伍赢下本轮
  • 卡牌大小:点数越大越强(比如2最小,A用14表示)

第一步:定义必要的类型

首先补全Suit和Card的类型定义,以及卡牌的比较逻辑:

import Data.List (maximumBy)

-- 定义四种花色
data Suit = Hearts | Diamonds | Clubs | Spades deriving (Eq, Ord, Show)
-- 定义卡牌:花色 + 点数(2-14,14代表A)
data Card = Card Suit Int deriving (Eq, Show)

-- 实现卡牌的大小比较:先比花色,再比点数
instance Ord Card where
  compare (Card s1 v1) (Card s2 v2) = case compare s1 s2 of
    EQ -> compare v1 v2
    x -> x

第二步:辅助函数实现

1. 把出牌列表按每4张分组

这个函数会把输入的卡牌列表拆分成每4张一组的轮次,不足4张的部分直接忽略:

groupIntoRounds :: [Card] -> [[Card]]
groupIntoRounds [] = []
groupIntoRounds xs
  | length xs >= 4 = take 4 xs : groupIntoRounds (drop 4 xs)
  | otherwise = []

2. 判断单轮的获胜队伍

给定压制花色、先手队伍编号(0或1)和一轮的4张牌,返回获胜队伍的编号:

determineRoundWinner :: Suit -> Int -> [Card] -> Int
determineRoundWinner trump firstPlayer [c1, c2, c3, c4] = 
  let -- 把每张牌和对应的队伍关联起来:先手队→对方队→先手队→对方队
      cardTeams = zip [firstPlayer, 1-firstPlayer, firstPlayer, 1-firstPlayer] [c1, c2, c3, c4]
      -- 筛选出本轮所有压制花色的牌
      trumpCards = filter (\(_, Card s _) -> s == trump) cardTeams
  in case trumpCards of
       -- 有压制花色的牌:选点数最大的那张的队伍
       _:_ -> fst $ maximumBy (\(_, cA) (_, cB) -> compare cA cB) trumpCards
       -- 没有压制花色:选首牌花色中点数最大的牌的队伍
       [] -> let leadSuit = suitOf c1
                 leadCards = filter (\(_, Card s _) -> s == leadSuit) cardTeams
             in fst $ maximumBy (\(_, cA) (_, cB) -> compare cA cB) leadCards
  where
    suitOf (Card s _) = s
-- 处理异常情况:如果一轮牌不足4张,直接报错(也可以改成忽略,看你需求)
determineRoundWinner _ _ _ = error "每轮必须包含4张卡牌"

第三步:主函数winnedCards实现

这个函数会遍历所有轮次,累积两队的获胜卡牌,最后返回结果元组:

winnedCards :: Suit -> [Card] -> Int -> ([Card], [Card])
winnedCards _ [] _ = ([], []) -- 空出牌列表返回空元组
winnedCards trump cards firstPlayer = 
  let rounds = groupIntoRounds cards
      -- 递归处理每一轮,累积两队的获胜卡牌
      processRounds :: [[Card]] -> ([Card], [Card]) -> ([Card], [Card])
      processRounds [] acc = acc
      processRounds (r:rs) (team0, team1) = 
        let winner = determineRoundWinner trump firstPlayer r
            -- 把本轮的牌加到获胜队伍的列表里
            newAcc = case winner of
                      0 -> (team0 ++ r, team1)
                      1 -> (team0, team1 ++ r)
                      _ -> (team0, team1) -- 无效情况,保持原状态
        in processRounds rs newAcc
  in processRounds rounds ([], [])

测试例子

比如我们来测试一轮:

-- 测试用例:压制花色是黑桃(Spades),先手是队伍0
testRound = [Card Hearts 10, Card Diamonds 9, Card Spades 14, Card Clubs 8]
-- 本轮有一张黑桃A(队伍0的牌),所以队伍0赢下这一轮所有牌
testResult = winnedCards Spades testRound 0 
-- 输出:([Card Hearts 10,Card Diamonds 9,Card Spades 14,Card Clubs 8],[])

规则调整说明

如果你的实际规则和我假设的不一样,比如:

  • 先手玩家编号是具体的玩家编号(0-3),而非队伍编号:只需要修改cardTeams里的队伍映射逻辑,比如把玩家0/2归为一队,1/3归为另一队
  • 出牌顺序不同:调整cardTeams里的队伍顺序即可
  • 卡牌大小规则不同:修改Card的Ord实例就行

内容的提问来源于stack exchange,提问作者Arnau Van Boschken ArnauB

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:02:28