在SML中实现Yahtzee

时间:2019-05-03 15:59:03

标签: sml poker

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

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

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

(three-of-a-kind, 14)

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

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

我按升序对计数进行排序,以便新列表的最后一个数字为我提供有关可能是“手”的一些信息,其中包括:

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

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

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

我还没有实现所有情况,所以下面的代码是我目前拥有的,最后一个功能是尝试测试我是否朝着正确的方向前进,但是它将重命名为{{1 }},以便它可以接收输入。

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;

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

我想我需要退货,但是不是吗?

我还有一个问题是,我的思维过程是否很好,如果没有建议,将不胜感激。

谢谢!

2 个答案:

答案 0 :(得分:2)

有几个主要问题:

  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

答案 1 :(得分:1)

这就是我重构的方式。

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

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;如果有五个元素,则可以确定为yahtzee;如果最高频率为4,则可以确定为4个。等等。

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

编辑:在molbdnilo之后,我还将得分与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"

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