-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathUpdate.elm
199 lines (156 loc) · 5.51 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
module Update exposing (update)
import Msg exposing (Msg(..))
import Model exposing (..)
import Extras
import Random.Pcg as Random
import Ports
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
NewGame playerColouring ->
let
initialModel =
Model.defaultModel playerColouring
newModel =
if playerColouring == Plain then
initialModel
else
cpuTurn initialModel
in
( newModel, Cmd.none )
Place boardId ->
if model.gameState == InProgress then
case model.selected of
Just piece ->
let
newModel =
{ model
| board = Model.place piece boardId model.board
, rack = Model.removeFromRack piece model.rack
, selected = Nothing
}
in
if wouldBeTie newModel then
( model, alertNoTies )
else
advanceTurn newModel
Nothing ->
( model, Cmd.none )
else
( model, Cmd.none )
Flip boardId ->
if model.gameState == InProgress then
let
newModel =
applyFlipMove model boardId
in
if wouldBeTie newModel then
( model, alertNoTies )
else
advanceTurn newModel
else
( model, Cmd.none )
Select piece ->
( { model | selected = Just piece }, Cmd.none )
SetWidth width ->
( { model | width = width }, Cmd.none )
alertNoTies : Cmd Msg
alertNoTies =
Ports.alert "Moves that would create a tie are not allowed"
wouldBeTie : Model -> Bool
wouldBeTie model =
isCPULosingModel model
&& isUserLosingModel model
advanceTurn : Model -> ( Model, Cmd Msg )
advanceTurn newModel =
if isCPULosingModel newModel then
( { newModel | gameState = Win }, Cmd.none )
else if isUserLosingModel newModel then
( { newModel | gameState = Loss }, Cmd.none )
else
( cpuTurn newModel, Cmd.none )
type Move
= PlaceMove ( Piece, BoardId )
| FlipMove BoardId
getMoves : Colouring -> Model -> List Move
getMoves colouring model =
let
boardIds =
Model.getAvailableBoardIds model.board
placeMoves =
List.concatMap
(\piece ->
List.map ((,) piece)
boardIds
)
(Model.getAvailablePieces colouring model.rack)
|> List.map PlaceMove
flipMoves =
getFlippapleBoardIds colouring model.board
|> List.map FlipMove
in
(placeMoves ++ flipMoves)
|> List.filter (applyMove model >> wouldBeTie >> not)
|> Extras.shuffle (Random.initialSeed 42)
isCPULosingModel : Model -> Bool
isCPULosingModel model =
Model.lineExists model.playerColouring model.board
isUserLosingModel : Model -> Bool
isUserLosingModel model =
Model.lineExists (Model.oppositeColouring model.playerColouring) model.board
nextPlayerHasNoWinningMove : Model -> Move -> Bool
nextPlayerHasNoWinningMove model move =
let
potentialModel =
applyMove model move
potentialFutureMoves =
getMoves model.playerColouring potentialModel
in
case Extras.find (userWinningMove potentialModel) potentialFutureMoves of
Just _ ->
False
Nothing ->
True
userWinningMove : Model -> Move -> Bool
userWinningMove model move =
applyMove model move
|> isCPULosingModel
cpuWinningMove : Model -> Move -> Bool
cpuWinningMove model move =
applyMove model move
|> isUserLosingModel
cpuTurn : Model -> Model
cpuTurn model =
let
moves : List Move
moves =
getMoves (Model.oppositeColouring model.playerColouring) model
postMovementModel =
Extras.find (cpuWinningMove model) moves
|> Extras.orElseLazy (\() -> Extras.find (nextPlayerHasNoWinningMove model) moves)
|> Extras.orElseLazy (\() -> Random.step (Random.sample moves) (Random.initialSeed 42) |> Tuple.first)
|> Maybe.map (applyMove model)
|> Maybe.withDefault model
in
if isCPULosingModel postMovementModel then
{ postMovementModel | gameState = Win }
else if isUserLosingModel postMovementModel then
{ postMovementModel | gameState = Loss }
else
postMovementModel
applyMove : Model -> Move -> Model
applyMove model move =
case move of
PlaceMove pm ->
applyPlaceMove model pm
FlipMove fm ->
applyFlipMove model fm
applyPlaceMove : Model -> ( Piece, BoardId ) -> Model
applyPlaceMove model ( piece, boardId ) =
{ model
| board = Model.place piece boardId model.board
, rack = Model.removeFromRack piece model.rack
}
applyFlipMove : Model -> BoardId -> Model
applyFlipMove model boardId =
{ model | board = Model.flipBoardPiece boardId model.board }