iT邦幫忙

2021 iThome 鐵人賽

DAY 11
0

再加入 迴轉功能卡 進入勝利判斷之前
我想整理一下判斷勝利相關的方法

每天都發現昨天寫的很噁

目前是否執行 結束回合是由 maybe_end_round 方法的 guard 判斷當下 turn 是否大於 3 來決定的
我想與其把判斷勝利放在 maybe_end_round 裡面,不如我放在 maybe_end_round 前面,也由 turn 是否大於 3 來決定
看看有沒有比較好用。

把原本從 compare_score/3 開頭的方法改成下面

  defp maybe_add_wins(%{turn: turn} = game) when turn > 3 do
    scores = calculate_score(game)

    if scores.host > scores.guest do
      assign_to_player(game, :host, :wins, game.host.wins + 1)
    else
      assign_to_player(game, :guest, :wins, game.guest.wins + 1)
    end
  end

  defp maybe_add_wins(game), do: game

  defp calculate_score(%{host: %{desk: host_desk}, guest: %{desk: guest_desk}, round: round}) do
    [host_score, guest_score] =
      [host_desk, guest_desk]
      |> Enum.map(&Enum.slice(&1, (round - 1)..(round * 3 - 1)))
      |> Enum.map(&Enum.sum(&1))

    %{host: host_score, guest: guest_score}
  end

現在 handle_cast :play_card 方法多了拉到這個層級的 maybe_add_wins 方法

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

    {:noreply, game}
  end

每個都 maybe 好像有點瞎,都拿掉好了

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

    {:noreply, game}
  end

加入 迴轉卡

我現在才發現我在 struct 把迴轉卡叫 :turn ,跟回合的 turn 好像有點撞到,
改成 :reverse 又很長,不過為了避免各種誤會還是改一下好了

initial_hand = [1, 1, 2, 2, 3, 3, 4, 5, 6, :reverse, :reverse]

因為我們在計算分數是用加的
所以要再計算前把 reverse 拿掉

應該是放在這個計算方法

  defp calculate_score(%{host: %{desk: host_desk}, guest: %{desk: guest_desk}, round: round}) do
    [host_score, guest_score] =
      [host_desk, guest_desk]
      |> Enum.map(&Enum.slice(&1, (round - 1)..(round * 3 - 1)))
      |> Enum.map(&擋掉(:reverse))
      |> Enum.map(&Enum.sum(&1))

    %{host: host_score, guest: guest_score}
  end

我們可以用 Enum.reject(enumerable, fun) 方法

Enum.reject([1, 2, :reverse], fn card -> card == :reverse end)

就變成

  defp calculate_score(%{host: %{desk: host_desk}, guest: %{desk: guest_desk}, round: round}) do
    [host_score, guest_score] =
      [host_desk, guest_desk]
      |> Enum.map(&Enum.slice(&1, (round - 1)..(round * 3 - 1)))
      |> Enum.map(&Enum.reject(&1, fn card -> card == :reverse end))
      |> Enum.map(&Enum.sum(&1))

    %{host: host_score, guest: guest_score}
  end

再來是 :reverse 的效果是可以疊加的
我出一張 :reverse 你出一張 :reverse 的效果會抵銷 依此類推

我猜可能要先數 :reverse 有幾張,偶數就用沒事,
奇數就在比較分數的時候用一下 not 方法

not(true) #=> false
not(host_score > guest_score)

於是判斷是不是 host 贏的地方被我加了一個 apply_reverse/2 來決定要不要套用 not

  defp add_wins(%{turn: turn} = game) when turn > 3 do
    scores = calculate_score(game)

    if apply_reverse((scores.host > scores.guest), scores.reverse) do
      assign_to_player(game, :host, :wins, game.host.wins + 1)
    else
      assign_to_player(game, :guest, :wins, game.guest.wins + 1)
    end
  end

  defp apply_reverse(compare, true), do: not(compare)
  defp apply_reverse(compare, false), do: compare

至於這局要不要 套用我目前把做法放進 calculate_score 他有我們這次所需要的數值們,
很顯然的,這又讓calculate_socre這個方法做太多事了,沒關係我們先把作法變出來

  defp calculate_score(%{host: %{desk: host_desk}, guest: %{desk: guest_desk}, round: round}) do
    [host_cards, guest_cards] =
      Enum.map([host_desk, guest_desk], &Enum.slice(&1, (round - 1)..(round * 3 - 1)))

    [host_score, guest_score] =
      [host_cards, guest_cards]
      |> Enum.map(&Enum.reject(&1, fn card -> card == :reverse end))
      |> Enum.map(&Enum.sum(&1))

    reverse =
      [host_desk, guest_desk]
      |> Enum.map(&Enum.filter(&1, fn card -> card == :reverse end))
      |> List.flatten()
      |> length()
      |> rem(2) == 1

    %{host: host_score, guest: guest_score, reverse: reverse}
  end

在利用 slice 取得當前這局雙方的卡後,除了使用 reject 去掉 :reverse 繼續算分數外
還有用他們套進 filter 取出所有的 :reverse,用 flatten 把它們混再一起後
算長度除 2 在比比看 是不是 1,就可以得到這局要不要 reverse

最後在 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, :guest, 2
:ok
iex(5)> play_card pid, :host, :reverse
:ok
iex(6)> play_card pid, :guest, :reverse
:ok
iex(7)> play_card pid, :host, :reverse 
:ok
iex(8)> play_card pid, :guest, 3       
:ok
iex(9)> status pid
%Game{
  guest: %{
    desk: [2, :reverse, 3],
    hand: [1, 1, 2, 3, 4, 5, 6, :reverse],
    wins: 0
  },
  host: %{
    desk: [1, :reverse, :reverse],
    hand: [1, 2, 2, 3, 3, 4, 5, 6],
    wins: 1
  },
  round: 2,
  turn: 1
}

上一篇
10 有局數就可以打分數
下一篇
12 終止遊戲
系列文
連線網頁卡牌遊戲(Elixir, Phoenix, Liveview)32
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言