-
Notifications
You must be signed in to change notification settings - Fork 35
/
Copy pathround.ex
executable file
·146 lines (121 loc) · 3.87 KB
/
round.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
defmodule Blackjack.Round do
alias __MODULE__
alias Blackjack.Hand
defstruct [
:deck, :current_hand, :current_player_id, :all_players, :remaining_players,
:successful_hands, :instructions
]
@opaque t :: %Round{
deck: Blackjack.Deck.t,
current_hand: Hand.t,
current_player_id: player_id,
all_players: [player_id],
remaining_players: [player_id],
successful_hands: [%{player_id: player_id, play: Hand.t}],
instructions: [instruction]
}
@type instruction :: {:notify_player, player_id, player_instruction}
@type player_instruction ::
{:deal_card, Blackjack.Deck.card} |
:move |
:busted |
{:winners, [player_id]} |
:unauthorized_move
@type player_id :: any
@type move :: :stand | :hit
@spec start([player_id]) :: {[instruction], t}
def start(players_ids), do:
start(players_ids, Blackjack.Deck.shuffled())
@spec move(t, player_id, move) :: {[instruction], t}
def move(%Round{current_player_id: player_id} = round, player_id, move), do:
%Round{round | instructions: []}
|> handle_move(move)
|> instructions_and_state()
def move(round, player_id, _move), do:
%Round{round | instructions: []}
|> notify_player(player_id, :unauthorized_move)
|> instructions_and_state()
@doc false
def start(player_ids, deck) do
%Round{
deck: deck,
current_hand: nil,
current_player_id: nil,
all_players: player_ids,
remaining_players: player_ids,
successful_hands: [],
instructions: []
}
|> start_new_hand()
|> instructions_and_state()
end
defp start_new_hand(%Round{remaining_players: []} = round) do
winners = winners(round)
Enum.reduce(
round.all_players,
%Round{round | current_hand: nil, current_player_id: nil},
¬ify_player(&2, &1, {:winners, winners})
)
end
defp start_new_hand(round) do
round = %Round{round |
current_hand: Hand.new(),
current_player_id: hd(round.remaining_players),
remaining_players: tl(round.remaining_players)
}
{:ok, round} = deal(round)
{:ok, round} = deal(round)
round
end
defp handle_move(round, :stand), do:
round
|> hand_succeeded()
|> start_new_hand()
defp handle_move(round, :hit) do
case deal(round) do
{:ok, round} ->
round
{:busted, round} ->
round
|> notify_player(round.current_player_id, :busted)
|> start_new_hand()
end
end
defp deal(round) do
{:ok, card, deck} =
with {:error, :empty} <- Blackjack.Deck.take(round.deck), do:
Blackjack.Deck.take(Blackjack.Deck.shuffled())
{hand_status, hand} = Hand.deal(round.current_hand, card)
round = notify_player(
%Round{round | deck: deck, current_hand: hand},
round.current_player_id,
{:deal_card, card}
)
{hand_status, round}
end
defp hand_succeeded(round) do
hand_data = %{player_id: round.current_player_id, hand: round.current_hand}
%Round{round | successful_hands: [hand_data | round.successful_hands]}
end
defp winners(%Round{successful_hands: []}), do:
[]
defp winners(round) do
max_score = Enum.max_by(round.successful_hands, &(&1.hand.score)).hand.score
round.successful_hands
|> Stream.filter(&(&1.hand.score == max_score))
|> Stream.map(&(&1.player_id))
|> Enum.reverse()
end
defp notify_player(round, player_id, data), do:
%Round{round | instructions: [{:notify_player, player_id, data} | round.instructions]}
defp instructions_and_state(round), do:
round
|> tell_current_player_to_move()
|> take_instructions()
defp tell_current_player_to_move(%Round{current_player_id: nil} = round), do:
round
defp tell_current_player_to_move(round), do:
notify_player(round, round.current_player_id, :move)
defp take_instructions(round), do:
{Enum.reverse(round.instructions), %Round{round | instructions: []}}
end