首页 文章

如何使用F#处理卡片

提问于
浏览
4

我一直致力于使用F#对流行的纸牌游戏(情书)进行建模,以了解有关函数式编程的更多信息 .

module Game = 
open Cards
open Players

type Deck = Card list

let dealACard (deck:Deck) = 
    let randomGenerator = System.Random()
    let index = randomGenerator.Next deck.Length
    let card = deck.Item index
    (card, (deck |> List.filter((<>) card)))

let createPlayer playerNumber deck =
    let card, newDeck = dealACard deck
    ({cards=[card]; playerNumber=playerNumber}, newDeck)

我一直做得很好,直到我知道如何模拟如何绘制卡片 . 为了测试这个,我想从牌组中抽出所有牌 . 我的程序看起来像这样:

let deck = createDeck
while not deck.IsEmpty do
    let card, newDeck = dealACard deck
    // print the card
    // how do I update the deck?

任何帮助或反馈都会很棒 .

2 回答

  • 6

    您可以使用 List.sortBy 对牌组进行随机播放,然后在 dealACard 方法中执行头尾模式匹配以返回顶牌的 Option 和新牌组 None 如果牌组中没有其他牌 .

    type DealResult = {
        Card : Card
        Deck : Deck
    }
    
    let shuffle deck = 
        let random = new System.Random()
        deck |> List.sortBy (fun x -> random.Next())
    
    let dealACard deck =
        match deck with
        | [] -> None
        | card::restOfDeck -> Some { Card = card; Deck = restOfDeck }
    

    您还可以通过允许应用随机数生成函数来使 shuffle 成为更高阶函数

    let shuffle random deck =
        deck |> List.sortBy (fun x -> random())
    

    示例用法

    let deck = [{Rank = 1}; {Rank = 2}] |> shuffle
    //val deck : Card list = [{Rank = 2;}; {Rank = 1;}]
    
    let draw1 = deck |> dealACard
    //val draw1 : DealResult option = Some {Card = {Rank = 2;}; 
    //                                      Deck = [{Rank = 1;}];}
    
    let draw2 = match draw1 with 
                | Some d -> d.Deck |> dealACard
                | None -> None
    //val draw2 : DealResult option = Some {Card = {Rank = 1;};
    //                                Deck = [];}
    
    
    let draw3 = match draw2 with 
                | Some d -> d.Deck |> dealACard
                | None -> None
    //val draw3 : DealResult option = None
    

    每条评论增加

    要以不可变的方式跟踪卡组的当前状态,您可能会有某种递归函数接受卡组

    type DealResult = {
        Card : Card option
        Deck : Deck
    }
    
    let dealACard deck =
        match deck with
        | [] -> { Card = None; Deck = deck }
        | card::restOfDeck -> { Card = Some card; Deck = restOfDeck }
    
    let rec dealAllCards deck =
        let result = deck |> dealACard
        match result.Card with 
        | None -> printfn "Cards out"
        | Some c -> 
            printfn "%A" c
            result.Deck |> dealAllCards
    
    let deck = [(Two, Hearts); (Three, Hearts); (Four, Hearts)] |> shuffle
    
    dealAllCards deck
    //(Three, Hearts)
    //(Four, Hearts)
    //(Two, Hearts)
    //Cards out
    
  • 1

    F#列表是 immutable ,所以如果 deck.IsEmpty 开始 false ,它将永远保留 false . 但是,没有理由让事情变得如此复杂 .

    假设您有一个已排序的牌组 . 我们只使用三张牌作为例子,但假设它是一个完整的牌组:

    let deck =
        [
            { Suit = Hearts; Face = Queen }
            { Suit = Diamonds; Face = King }
            { Suit = Spades; Face = Ace }
        ]
    

    您可以使用随机数生成器轻松地对卡座进行加扰:

    let r = Random ()
    let scrambledDeck = deck |> List.sortBy (fun _ -> r.Next ())
    

    第一次创建 scrambledDeck 时,它在FSI中可能如下所示:

    > let scrambledDeck = deck |> List.sortBy (fun _ -> r.Next ());;
    
    val scrambledDeck : Card list =
      [{Suit = Spades;
        Face = Ace;}; {Suit = Hearts;
                       Face = Queen;}; {Suit = Diamonds;
                                        Face = King;}]
    

    但如果你再次这样做,它可能看起来像这样:

    > let scrambledDeck = deck |> List.sortBy (fun _ -> r.Next ());;
    
    val scrambledDeck : Card list =
      [{Suit = Spades;
        Face = Ace;}; {Suit = Diamonds;
                       Face = King;}; {Suit = Hearts;
                                       Face = Queen;}]
    

    现在你有一个混乱的牌组,你可以简单地开始拉出牌,例如为了打印它们:

    scrambledDeck |> List.iter (printfn "%O")
    

相关问题