10 有局数就可以打分数

昨天做到双方都出卡後,增加 turn 数
现在在做 每到 3 turn 换一局 round + 1

昨天做到这边

  def handle_cast({:play_card, player, card}, game) do
    game =
      game
      |> play_card_for(player, card)
      |> maybe_end_turn()

    {:noreply, game}
  end

  defp play_card_for(game, player, card) do
    data =
      game
      |> Map.get(player)
      |> then(&Map.merge(&1, %{hand: &1.hand -- [card], desk: &1.desk ++ [card]}))

    Map.replace(game, player, data)
  end

  defp maybe_end_turn(%{guest: guest, host: host} = game) do
    if length(guest.desk) == length(host.desk) do
      Map.merge(game, %{turn: game.turn + 1})
    else
      game
    end
  end

再来一局

也许这个可以不要跟 turn 同一个方法
来写一个 maybe_end_round 好了

  defp maybe_end_round(%{turn: turn} = game) when turn > 3 do
      Map.merge(game, %{turn: 1, round: game.round + 1})
  end
  defp maybe_end_round(game), do: game

这是什麽巫术,怎麽有 do: 然後没有 end
先从为什麽要写两个 maybe_end_round 开始
还不习惯这个的时候可以会写出

  defp maybe_end_round(%{turn: turn} = game) do
    if turn > 3 do
      Map.merge(game, %{turn: 1, round: game.round + 1})
    else
      game
    end
  end

我们先利用 when 把简单的判断拉到 pattern matching 方法层面的 guards
这样子这个方法除了变数 match 之外,when 的条件也要符合,但 when 只能使用基本的方法 详见 Guards 文件

拉出来之後方法可以变成两个

  defp maybe_end_round(%{turn: turn} = game) when turn > 3 do
    Map.merge(game, %{turn: 1, round: game.round + 1})
  end

  defp maybe_end_round(game) do
    game
  end

再把只回传 game 没有做别的事的小方法改写成一行,用 do:
至於为什麽可以这样写,牵扯到 macro
这个比较进阶,我也还在摸索,但在这次应该是不会用到,
我们先记得有这个可以简化单行方法就好

把他加在 handle_cast :play_card 後面

  def handle_cast({:play_card, player, card}, game) do
    game =
      game
      |> play_card_for(player, card)
      |> maybe_end_turn()
      |> maybe_end_round()

    {:noreply, game}
  end

在 iex 试试看

iex(1)> import Game
Game
iex(2)> {:ok, pid} = start
{:ok, #PID<0.113.0>}
iex(3)> play_card pid, :host, 3
:ok
iex(4)> play_card pid, :guest, 4
:ok
iex(5)> play_card pid, :host, 1 
:ok
iex(6)> play_card pid, :guest, 2
:ok
iex(7)> play_card pid, :host, 1 
:ok
iex(8)> play_card pid, :guest, 5
:ok
iex(9)> status pid
%Game{
  guest: %{desk: [4, 2, 5], hand: [1, 1, 2, 3, 3, 6, :turn, :turn], wins: 0},
  host: %{desk: [3, 1, 1], hand: [2, 2, 3, 4, 5, 6, :turn, :turn], wins: 0},
  round: 2,
  turn: 1
}

成功

不可以偷出牌

另外还有就是,我们可能要限制一下,同一个人不能连续出牌,要等双方都出完,完成一回合才能再往下,
这点也许後面会改,但是我们先把它做好来。

  def handle_cast({:play_card, :host, _card}, %{host: host, guest: guest} = game)
      when length(host.desk) > length(guest.desk),
      do: {:noreply, game}

  def handle_cast({:play_card, :guest, _card}, %{host: host, guest: guest} = game)
      when length(host.desk) < length(guest.desk),
      do: {:noreply, game}

这次我写在原本的 handle_cast :play_card 前面来拦截,
假如 host 出牌,可是他牌桌上的牌数已经比对方多了,这代表他是连续出,所以就忽略这次动作,反之亦然
虽然我觉得没有比起前面收成方法叠再一起,但先这样吧

iex 试试看

iex(1)> import Game
Game
iex(2)> {:ok, pid} = start
{:ok, #PID<0.113.0>}
iex(3)> play_card pid, :host, 1
:ok
iex(4)> play_card pid, :host, 1
:ok
iex(5)> status pid
%Game{
  guest: %{desk: [], hand: [1, 1, 2, 2, 3, 3, 4, 5, 6, :turn, :turn], wins: 0},
  host: %{desk: [1], hand: [1, 2, 2, 3, 3, 4, 5, 6, :turn, :turn], wins: 0},
  round: 1,
  turn: 1
}

完成拉

算个输赢

我们有出卡,有回合,好像可以来算个输赢,
今天先撇开特殊卡片,假如只出数字卡的情况。
在换 round 的时候,来算一下上一 round 的结果,
谁赢就在谁的 wins 里面 + 1

  defp maybe_end_round(%{turn: turn} = game) when turn > 3 do
    game
    |> Map.merge(%{turn: 1, round: game.round + 1})
    |> compare_score(score(:host, game), score(:guest, game))
  end

这里加一个 compare_score/3 收 game, host分数, guest分数
分数的部分也另外写一个 socre 方法算

  # 这两个方法用 when 里面来判断谁的点数大
  defp compare_score(game, host_score, guest_score) when host_score > guest_score,
    do: add_wins(game, :host)

  defp compare_score(game, host_score, guest_score) when host_score < guest_score,
    do: add_wins(game, :guest)

  defp add_wins(game, player) do
  # 这个方法帮获胜者加一胜
    player_data =
      game
      |> Map.get(player)
      |> then(&Map.merge(&1, %{wins: &1.wins + 1}))

    Map.replace(game, player, player_data)
  end

  defp score(player, %{round: round} = game) do
    range_start = (round - 1) * 3
    range = range_start..(range_start + 2)
    
    # range 是依照回合要从桌上拿的卡片范围
    # 1 局 就是从 desk 里面利用 slice 方法拿 0..2 位置的卡片
    # 2 局 就是 3..5 位置的卡片,依此类推

    game
    |> Map.get(player)
    |> Map.get(:desk)
    |> Enum.slice(range)
    |> Enum.sum() #最後用 sum 计算总和,回传分数
  end

好像离可以玩的状态,好拉在 iex 可以玩的状态不远了。
再撑一下,这个核心的地方完成之後,我们做介面会比较放心。


<<:  Day09 建造APP(3)

>>:  D-6. Model scope & 建立搜索功能

[Day 3] php阵列与资料结构

php中阵列的写法 一般阵列 其索引值形式为从0开始往後累加的数字(0、1、2、3...) $fru...

[经典回顾]走骇客的路让骇客无路可走?

通讯软件供应者的职业道德与国家执行公务(反恐)哪个重要? 着问题...不才在下废宅本人还没有想好答案...

Day 28 数据可视化DataV-1

笔者昨日与三五好友相约在猫空泡茶思考着今天想写些什麽,半夜泡茶的群众着实不少,整个山头上车水马龙,...

[D22] 物件侦测(3)

上一篇我们认识了基本的 R-CNN 和 Fast-RCNN,接着来看 Faster RCNN! Fa...

Day06 - 根据需求画出 Flowchart 或是 State Diagram

今天尝试将 2D RPG 角色移动模组根据规格画出 Flowchart 和 State Diagra...