Haskell按4元素迭代列表:实现winnedCards函数技术问询
嘿,我来帮你搞定这个Haskell卡牌游戏的函数实现!先理清楚需求和规则,然后一步步写代码~
实现思路与代码
首先,咱们先明确游戏规则的合理假设(如果和你的实际规则有出入,调整起来也很简单):
- 两队对战,每轮出4张牌:先手队出2张,对方队出2张,出牌顺序是「先手→对方→先手→对方」
- 胜负判定:
- 优先看压制花色(trump suit):如果本轮有压制花色的牌,点数最大的那张牌所属的队伍赢下本轮所有4张牌
- 如果没有压制花色,就看本轮第一张牌的花色(先手队的首张牌),该花色中点数最大的牌所属队伍赢下本轮
- 卡牌大小:点数越大越强(比如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
相关产品推荐
相关产品推荐

