9 结束这回合

今天我们可能可以来做一下回合,毕竟要算分数还是干嘛都是建立在回和上面。

目前我们的 game struct 长这样,已经有预留 round 跟 turn

%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
}

这次要做的事情有

  1. 双方都出牌後,turn + 1
  2. 第 3 turn 结束後 turn 变回 1, round + 1

感觉这件事情是要放在出牌的方法里面
双方都出牌这个条件,应该是出完牌检查两个人的 desk 长度是不是一样

  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

elixir 在 module 定义 private 方法,就是只有在这个 module 里面才可以呼叫的方法,是用 defp

我们在这个 maybe_end_turn 方法的变数收 game struct
方法的结果也是回传 game struct
所以我们在 handle :play_card 方法里面可以这样子接

  def handle_cast({:play_card, :host, card}, %{host: host} = game) do
    game =
      game
      |> Map.replace(:host, play_card_helper(host, card))
      |> maybe_end_turn()

    {:noreply, game}
  end

这里有一个新符号 |> 这个像三角形的东西叫 pipe operator ,可能要直接用范例比较好解释

defmodule MyMath do
  def add_one(number) do
    number + 1
  end
end

今天我们有一个 add_one/1 方法,他收一个变数,并回传变数加一为结果
假如我要用 3 次

MyMath.add_one(MyMath.add_one(MyMath.add_one(1)))

或是

first_add = MyMath.add_one(1)
second_add = MyMath.add_one(first_add)
result = MyMath.add_one(second_add)

这两个写法都让人很烦躁,於是我们用 pipe operator 看看

1
|> MyMath.add_one()
|> MyMath.add_one()
|> MyMath.add_one()

|> 之後的方法的第一个变数,会自动填入 |> 左边的结果
所以如果原本有两个变数,串在 |> 里面的时候,只需要填第二个。

因为这样子写的时候配合适当的方法名称,读起来会像句子

水饺皮
|> 加馅(水饺馅)
|> 包起来

懂 pipe 之後我们再回来看

  def handle_cast({:play_card, :host, card}, %{host: host} = game) do
    game =
      game
      |> Map.replace(:host, play_card_helper(host, card))
      |> maybe_end_turn()

    {:noreply, game}
  end
新的game =
  旧的game
  |> 出卡方法
  |> 回合方法

感觉出卡方法应该可以在变得更清楚一些,像是

  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

整理完之後,在 handle_cast 里面只留下比较好读的方法名称,
细部的做法都丢进 private 方法中。
也顺便把两个 handle_cast :play_card 合成一个。

最後来 iex 试试看
我们这次进 iex 之後先 import Game 这样我们就可以直接呼叫 Game module 里面的方法

$ iex game.ex
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)> status pid
%Game{
  guest: %{desk: [4], hand: [1, 1, 2, 2, 3, 3, 5, 6, :turn, :turn], wins: 0},
  host: %{desk: [3], hand: [1, 1, 2, 2, 3, 4, 5, 6, :turn, :turn], wins: 0},
  round: 1,
  turn: 2
}

成功了,双方都出牌之後,turn 就 + 1 了

round 留到明天好了哈哈,掰掰


<<:  RISC V::中断与异常处理 -- PLIC 介绍

>>:  Day 17: 人工神经网路初探 损失函数(下)

30-7 之分层架构 From Patterns of Enterprise Application Architecture

接下来的几篇文章,我们将要几乎可以说是软件架构模式始祖 ( 大概 ) 的这本书 : 企业应用架构模式...

DAY17 机器学习专案实作-员工离职预测(中)

ㄧ、资料前处理 1. 补值、删值 前面我们透过视觉化的方式找到资料有缺值,因此我们要将资料进行补值。...

【Day2】Information Security Overview

随着新的科技环境变化, 资讯安全也会变得更多面向。 根据NIST(美国国家标准暨技术研究院)定义的电...

力抗暗黑,Azure 资安天使的逆袭

第一次出书新手上路,请多多指教 谢谢第十届我们的Diablo, the rest of the li...

自我笔记 - django 系列 [基础篇]

django 基础篇 主要说明如何创建一个基本的 django 服务。 基本工具 env虚拟机 - ...