-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathUpdate.elm
205 lines (158 loc) · 5.42 KB
/
Update.elm
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
module Update exposing (update)
import Msg exposing (Msg(..))
import Model exposing (Model, PieceColour(..), Board, RackId(..), BoardId(..), Size(..), defaultState)
import Material
import Ports
import Extras
import Random.Pcg as Random exposing (Seed)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
NoOp ->
( model, Cmd.none )
NewGame players ->
( { defaultState | players = players, muted = model.muted }, Cmd.none )
Place boardId ->
if Model.isFree model.board boardId then
case model.selected of
Nothing ->
( model, Cmd.none )
Just rackId ->
case Model.placeAt Blue rackId boardId model of
Nothing ->
( model, Cmd.none )
Just newModel ->
( cpuMoves { newModel | selected = Nothing }
, if model.muted then
Cmd.none
else
Ports.sound "clack"
)
else
( model, Cmd.none )
Mdl msg' ->
Material.update msg' model
Select rackId ->
( { model | selected = Just rackId }, Cmd.none )
ToggleMute ->
( { model | muted = not model.muted }, Cmd.none )
cpuMoves : Model -> Model
cpuMoves model =
let
rackList =
case model.players of
2 ->
[ Red ]
3 ->
[ Green, Red ]
_ ->
[ Green, Red, Yellow ]
in
List.foldl takeTurn model rackList
takeTurn : PieceColour -> Model -> Model
takeTurn pieceColour model =
let
maybeWinner =
Model.getWinner model.board
maybeMove =
findMove pieceColour model
in
case ( maybeWinner, maybeMove ) of
( Nothing, Just ( rackId, boardId ) ) ->
Model.placeAt pieceColour rackId boardId model
|> Maybe.withDefault model
_ ->
model
findMove : PieceColour -> Model -> Maybe ( RackId, BoardId )
findMove pieceColour model =
let
moves =
getAvailableMoves pieceColour model
|> shuffle (Random.initialSeed 42)
maybeWinningMove =
Extras.find (winningMove pieceColour model) moves
in
case maybeWinningMove of
Nothing ->
case
let
blockingBoardIds =
getBlockingBoardIds (otherColours pieceColour) model.board
in
Extras.find (\( _, boardId ) -> List.member boardId blockingBoardIds) moves
of
Nothing ->
Random.step (Random.sample moves) (Random.initialSeed 42)
|> fst
move ->
move
move ->
move
winningMove : PieceColour -> Model -> ( RackId, BoardId ) -> Bool
winningMove pieceColour model ( rackId, boardId ) =
case
Model.placeAt pieceColour rackId boardId model
`Maybe.andThen` (Model.getWinner << .board)
of
Nothing ->
False
Just a ->
True
getBlockingBoardIds : List PieceColour -> Board -> List BoardId
getBlockingBoardIds pieceColours board =
List.concatMap (Model.getBlockingBoardIdsForColour board) pieceColours
otherColours : PieceColour -> List PieceColour
otherColours pieceColour =
List.filter ((/=) pieceColour) Model.pieceColourPossibilities
shuffle : Seed -> List a -> List a
shuffle seed list =
let
length =
List.length list
randomTags =
Random.step (Random.list length (Random.int 0 length)) (Random.initialSeed 42)
|> fst
in
List.map2 (,) randomTags list |> List.sortBy fst |> List.unzip |> snd
getAvailableMoves : PieceColour -> Model -> List ( RackId, BoardId )
getAvailableMoves pieceColour model =
getAvailableRackIds pieceColour model
|> atMostOneOfEachSize
|> List.concatMap (getAvailableMovesForRackId model.board)
getAvailableRackIds pieceColour model =
let
rack =
Model.getRackByPieceColour pieceColour model
in
List.filterMap
(\rackId ->
if Model.getRackSectionValue pieceColour rackId model then
Just rackId
else
Nothing
)
Model.rackIdPossibilities
atMostOneOfEachSize : List RackId -> List RackId
atMostOneOfEachSize rackIds =
[ Extras.find isLarge rackIds
, Extras.find isMedium rackIds
, Extras.find isSmall rackIds
]
|> List.filterMap identity
isLarge : RackId -> Bool
isLarge (RackId _ size) =
size == Large
isMedium : RackId -> Bool
isMedium (RackId _ size) =
size == Medium
isSmall : RackId -> Bool
isSmall (RackId _ size) =
size == Small
getAvailableMovesForRackId : Board -> RackId -> List ( RackId, BoardId )
getAvailableMovesForRackId board rackId =
let
(RackId _ size) =
rackId
in
Model.getAllEmptySpacesOfSize board size
|> List.map ((,) rackId)