昨天的进度
defmodule Game do
defstruct rounds: [], host_hand: [], guest_hand: [], round: 1, winner: nil
use GenServer
def init(status) do
{:ok, status}
end
def handle_call(:get_status, _from, status) do
{:reply, status, status}
end
end
我们今天补上 client 这边的方法
来取代 GenServer.start_link(Game, %Game{})
与 GenServer.call(game_pid, :get_status)
我们一样放在 Game module 里面
defmodule Game do
# ...上略
def handle_call(:get_status, _from, status) do
{:reply, status, status}
end
# Client 方法
def start do
GenServer.start_link(__MODULE__, %Game{})
end
def status(game_pid) do
GenServer.call(game_pid, :get_status)
end
end
在 start 方法里面我们使用了 __MODULE__
,这个是指方法所在的 module 在这边即是 Game
我们再来 iex 试试看吧
$ iex game.ex
iex(1)> {:ok, pid} = Game.start
{:ok, #PID<0.115.0>}
iex(2)> Game.status(pid)
%Game{guest_hand: [], host_hand: [], round: 1, rounds: [], winner: nil}
看起来都正常
在我试做出牌方法的时候,我发现有些东西不太妥当,尤其是 Game struct,原本的结构超烂,
变得很难用非常直觉的方式去更改场上的卡片
我们来更新一下,在我们还没使用到任何逻辑的情况,这次是无痛升级
defmodule Game do
initial_hand = [1, 1, 2, 2, 3, 3, 4, 5, 6, :turn, :turn]
defstruct host: %{desk: [], hand: initial_hand, wins: 0},
guest: %{desk: [], hand: initial_hand, wins: 0},
turn: 1, round: 1
# 以下省略
这个作法我们也不需要另一个 Round struct,可以把它整个移除
接着是 在各个 GenServer 方法内,我想把 status 这个变数改成 game
毕竟那个变数永远都会是 Game struct 就直接用游戏本身比较贴切
於是变成这样:
# 上略
def init(game) do
{:ok, game}
end
def handle_call(:get_status, _from, game) do
{:reply, game, game}
end
def start do
GenServer.start_link(__MODULE__, %Game{})
end
def status(pid) do
GenServer.call(pid, :get_status)
end
end
我们先来看看出一张牌需要改什麽
原本的 game 是这样
%Game{
guest: %{desk: [], hand: [1, 1, 2, 2, 3, 3, 4, 5, 6, :turn, :turn], wins: 0},
host: %{desk: [], hand: [1, 1, 2, 2, 3, 3, 4, 5, 6, :turn, :turn], wins: 0},
round: 1,
turn: 1
}
假如 host 出一张 1 ,那我们要做的事情有
这次我们用 handle_cast 来做,
并且按步骤一步一步做做看
def play_card(pid, :host, card) do
GenServer.cast(pid, {:play_card, :host, card})
end
def handle_cast({:play_card, :host, card}, game) do
%{host: host} = game
# 从 hand 里面拿掉一张 1
new_hand = host.hand -- [card]
# 把 1 加在 host 的 desk 里面
new_desk = host.desk ++ [card]
# 组成新的 host 来替换旧的
new_host = Map.merge(host, %{hand: new_hand, desk: new_desk})
# 组成新的 game 来替换旧的
new_game = Map.replace(game, :host, new_host)
# 让 process 使用新的 game
{:noreply, new_game}
end
我们来用 iex 验证看看
$ iex game.ex
iex(1)> {:ok, pid} = Game.start
{:ok, #PID<0.112.0>}
iex(2)> Game.status pid
%Game{
guest: %{desk: [], hand: [1, 1, 2, 2, 3, 3, 4, 5, 6, :turn, :turn], wins: 0},
host: %{desk: [], hand: [1, 1, 2, 2, 3, 3, 4, 5, 6, :turn, :turn], wins: 0},
round: 1,
turn: 1
}
iex(3)> Game.play_card pid, :host, 1
:ok
iex(4)> Game.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
}
在我们执行完出牌後,host 的桌面多了 1 ,手牌少了一张 1 , 正是我们要的,
但是我们还没有考虑到 guest 的情况,也写得比较不像 elixir 的做法,
下一篇就来稍微重构一下出牌方法。
案例说明及适用场景 客户往来记录,属於商机管理的一环,一个客户不见得袛有一次商机,而一个商机的完成必...
前言 前一篇搞定 Ubuntu 作业系统的安装,接下来我们继续安装『机器学习』的相关软件及工具,包括...
工程师太师了: 第13.5话 杂记: 蓝画面指的是Windows崩溃停止执行时出现的蓝底白字画面。 ...
区间突破的策略,是顺势交易中重要的一环 ORB策略(Open Range Breakout syst...
物件导向程序设计是程序设计中极为重要的一环,其基本概念为物件及类别。 类别定义事物的特点,物件为事件...