今天我們可能可以來做一下回合,畢竟要算分數還是幹嘛都是建立在回和上面。
目前我們的 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
}
這次要做的事情有
感覺這件事情是要放在出牌的方法裡面
雙方都出牌這個條件,應該是出完牌檢查兩個人的 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 留到明天好了哈哈,掰掰