【问题标题】:Implementing Yahtzee in SML在 SML 中实现 Yahtzee
【发布时间】:2019-05-03 15:59:02
【问题描述】:

我正在编写一个 SML Yahtzee/Poker 程序,它接收一个列表作为格式的输入

yahtzee(1,3,3,4,3)

其中每个数字是掷骰子的结果,然后打印相应的结果,在这种情况下为

(three-of-a-kind, 14)

最后一个数字是在这种情况下作为输入接收的所有 5 个骰子的总和。

我目前使用字符串并使用计数变量计算每个数字的出现次数,因为掷骰子有 6 种可能的结果 - 1、2、3、4、5 或 6。

我按升序对计数进行排序,以便我的新列表的最后一个数字为我提供一些关于可能是什么“手”的信息,如下所示:

yahtzee - 这是 5 种,“价值”50 分,所以回报是 (yahtzee, 50) 大直 - 5 数字序列,价值 40 点所以返回 (large-straight, 40) 小直 - 4 数字序列,30 点所以 (small-straight, 30) 满屋 - 3 种和另一种,25 分所以(full-house, 25) 四种——价值与所有 5 个骰子的总和一样多,所以(four-of-a-kind, sum) 三样 - 与所有 5 个骰子的总和一样多,所以(three-of-a-kind, sum) 机会 - (chance, sum)

因为我也不关心出现 3 次的数字,例如,因为无论如何它都会是三种。通过检查最后一个数字,我可以很好地了解正在发生的事情。

对于满堂彩,如果我检查最后一个数字之前的数字等于 2,我知道这是满堂彩,因为最后一个将是 3。重要的是要注意,如果总和满堂彩的点数大于 25,但是,那手牌应该是三点,因为它产生的点数更多。

我还没有实现所有案例,所以下面的代码是我目前拥有的,最后一个函数是尝试测试我是否朝着正确的方向前进,但将重命名为yahtzee(L)稍后它可以接收输入。

fun counter (nil, count1:int, count2:int, count3:int, count4:int, count5:int, count6:int) = ListMergeSort.sort (fn (x,y) => x > y) [count1, count2, count3, count4, count5, count6]
    | counter (6::t, count1, count2, count3, count4, count5, count6) = counter (t, count1, count2, count3, count4, count5, count6+1)
    | counter (5::t, count1, count2, count3, count4, count5, count6) = counter (t, count1, count2, count3, count4, count5+1, count6)
    | counter (4::t, count1, count2, count3, count4, count5, count6) = counter (t, count1, count2, count3, count4+1, count5, count6)
    | counter (3::t, count1, count2, count3, count4, count5, count6) = counter (t, count1, count2, count3+1, count4, count5, count6)
    | counter (2::t, count1, count2, count3, count4, count5, count6) = counter (t, count1, count2+1, count3, count4, count5, count6)
    | counter (1::t, count1, count2, count3, count4, count5, count6) = counter (t, count1+1, count2, count3, count4, count5, count6)
    | counter (h::t, count1, count2, count3, count4, count5, count6) = ListMergeSort.sort (fn (x,y) => x > y) [count1, count2, count3, count4, count5, count6];

fun sum (nil) = 0
    | sum (h::t) = h + sum(t)

fun anyOfAKind (nil) = 0
    | anyOfAKind (L) = List.last(L)

fun fullHouse (nil) = 0
    | fullHouse (L) = List.nth(L, 3)

fun testdice (nil) = []
    | testdice (L) = 
        let 
            val listSum = sum(L)
            val count = counter(L,0,0,0,0,0,0)
        in
            if fullHouse(count) = 2 then
                if listSum <= 25 then print "(fullhouse, 25)"
                else print "(threeofakind, " ^ Int.toString (sum) ^ ")"
        end;

如果我删除最后一个函数,则构建文件没有问题,但是,当最后一个函数到位时,我会收到错误消息

Error: syntax error found at END

我的第一个问题是,为什么我会收到此错误消息?

我想我需要退货但不是?

我的另一个问题是我的思维过程是否看起来不错,如果没有任何建议,我们将不胜感激。

谢谢!

【问题讨论】:

  • 您收到语法错误,因为您的 ifs 之一缺少其 else 子句。
  • 另一个问题是testdice的一个子句产生一个列表,而另一个产生unit

标签: sml poker


【解决方案1】:

有几个主要问题:

  1. 您的if 没有匹配的else。那是你的语法问题。
  2. 一旦你得到排序,testdice 想要生成一个列表,或者打印一些东西并生成unit。函数的子句不能有不同的类型。
  3. nil 只是一个名字,在一个模式中它匹配所有的东西。它不是数据构造函数 - “空列表”构造函数是 []

我将假设该函数实际上应该生成一对,而不是打印具有特定格式的字符串。
正如你所说,我还假设输入是一个列表 - 而不是一个五元组,如示例中所示。
(从一种形式改写成另一种形式并不难。)

首先,定义一个合适的数据类型:

datatype Hand = Yahtzee
              | LargeStraight
              | SmallStraight
              | FullHouse
              | Four
              | Three
              | Chance;

评分函数将产生一个Hand * int 对。

有很多方法可以继续;我选择基于结构而不是计数来编写分类函数。

(* Utility function; 'all xs' is true if and only if none of its elements are false. *)
fun all [] = true
  | all (false :: ts) = false
  | all (_ :: ts) = all ts

(* These check the conditions for each type of roll. They all assume that the input is sorted. *)
fun is_yahtzee [a,b,c,d,e] = all [a = b, b = c, c = d, d = e]
  | is_yahtzee _ = false;

fun is_large_straight [a,b,c,d,e] = all [b = a + 1, c = b + 1, d = c + 1, e = d + 1]
  | is_large_straight _ = false;

fun is_small_straight [a,b,c,d,e] = all [b = a + 1, c = b + 1, d = c + 1, e <> d + 1]
                             orelse all [b <> a + 1, c = b + 1, d = c + 1, e = c + 1]
  | is_small_straight _ = false;

fun is_full_house [a,b,c,d,e] = all [a = b, b = c, c <> d, d = e]
                         orelse all [a = b, b <> c, c = d, d = e]
  | is_full_house _ = false;

fun is_four [a,b,c,d,e] = all [a = b, b = c, c = d, d <> e]
                   orelse all [a <> b, b = c, c = d, d = e]
  | is_four _ = false;

fun is_three [a,b,c,d,e] = all [a = b, b = c, c <> d, d <> e]
                    orelse all [a <> b, b = c, c = d, d <> e]
                    orelse all [a <> b, b <> c, c = d, d = e]
  | is_three _ = false;

有了这个,你可以用一种非常简单的方式把你的评分规则写成一个函数:

(* Assumes that the input is sorted and has five elements. *)
fun score roll =
    let val value = sum roll in
        if is_yahtzee roll
        then (Yahtzee, 50)
        else if is_large_straight roll
        then (LargeStraight, 40)
        else if is_small_straight roll
        then (SmallStraight, 30)
        else if is_full_house roll
        then (if value > 25 then (Three, value) else (FullHouse, 25))
        else if is_four roll
        then (Four, value)
        else if is_three roll
        then (Three, value)
        else (Chance, value)
    end

【讨论】:

    【解决方案2】:

    这就是我可能会如何重构它。

    为了更好地确定我的点数,我想建立一个骰子的直方图:

    fun histogram [] = []
      | histogram (d::ds) =
        let
          fun insert x [] = [(x, 1)]
            | insert x ((y,n) :: hist) =
                if x = y
                then (y,n+1) :: hist
                else (y,n) :: insert x hist
        in
          insert d (histogram ds)
        end
    
    fun frequencyOf (x, []) = 0
      | frequencyOf (x, (y,n)::hist) =
          if x = y
          then n
          else frequencyOf (x, hist)
    
    fun highestFrequency [] = 0
      | highestFrequency ((x,i)::hist) =
          Int.max (i, highestFrequency hist)
    

    这样,如果直方图中只有一个元素,我可以确定一个 yahtzee,如果有五个,那么我可以确定一个直线,如果最高频率是 4,那么我可以确定四个,依此类推。

    然后我想要一个自定义数据类型来表示结果的种类,

    编辑:继 molbdnilo 之后,我也将 score 与 roll 类型完全分开。

    datatype roll
      = Yahtzee
      | LargeStraight
      | SmallStraight
      | FullHouse
      | FourOfAKind
      | ThreeOfAKind
      | Chance
    

    为了确定结果,

    fun determineDice (d1, d2, d3, d4, d5) =
        let
          val dice = ListMergeSort.sort (op >) [d1, d2, d3, d4, d5]
          val diceHistogram = histogram dice
        in 
          case length diceHistogram of
               1 => (Yahtzee, 50)
             | 2 => if highestFrequency diceHistogram = 4
                    then (FourOfAKind, sum dice)
                    else (FullHouse, 25)
             | 3 => if highestFrequency diceHistogram = 3
                    then (ThreeOfAKind, sum dice)
                    else (Chance, sum dice)
             | 4 => (Chance, sum dice)
             | 5 => if frequencyOf (6, dice) = 0
                    then (SmallStraight, 30)
                    else (LargeStraight, 40)
        end
    

    为了打印掷骰结果,我还会编写辅助函数:

    fun showRoll Yahtzee = "Yahtzee"
      | showRoll LargeStraight = "Large straight"
      | ...
      | showRoll (Chance _) = "Chance"
    
    fun showRollWithPoints (roll, points) =
        showRoll roll ^ " of " ^ Int.toString points ^ " points"
    

    您可以进行的其他一些改进:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-03-23
      • 2013-01-11
      • 2021-04-07
      • 2018-03-30
      • 2015-11-08
      • 2012-10-27
      • 2021-06-25
      • 2015-01-16
      相关资源
      最近更新 更多