-
Notifications
You must be signed in to change notification settings - Fork 0
/
Update.elm
181 lines (142 loc) · 5 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
module Update exposing (update, getMoves)
import Msg exposing (Msg(..))
import Model exposing (..)
import Extras
import Random.Pcg as Random
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
NewGame ->
( Model.defaultModel, Cmd.none )
Place pinId ->
if model.gameState == InProgress then
let
newModel =
{ model
| board = Model.place White pinId model.board
, rack = Model.removeFromRack White model.rack
}
in
if isCPULosingModel newModel then
( { newModel | gameState = Win }, Cmd.none )
else if isUserLosingModel newModel then
( { newModel | gameState = Loss }, Cmd.none )
else if isTieModel newModel then
( { newModel | gameState = Tie }, Cmd.none )
else
( cpuTurn newModel, Cmd.none )
else
( model, Cmd.none )
type alias Move =
( Ball, PinId )
getMoves : Ball -> Rack -> Board -> List Move
getMoves ball rack board =
List.map ((,) ball)
(Model.getAvailablePinIds board)
|> Extras.shuffle (Random.initialSeed 42)
isCPULosingModel : Model -> Bool
isCPULosingModel model =
let
rack =
model.rack
in
if rack.red <= 0 && rack.white <= 0 then
currentScore Red model.board <= currentScore White model.board
else
False
isUserLosingModel : Model -> Bool
isUserLosingModel model =
let
rack =
model.rack
in
if rack.red <= 0 && rack.white <= 0 then
Model.currentScore White model.board <= Model.currentScore Red model.board
else
False
isTieModel : Model -> Bool
isTieModel model =
let
rack =
model.rack
in
if rack.red <= 0 && rack.white <= 0 then
Model.currentScore White model.board == Model.currentScore Red model.board
else
False
nextPlayerHasNoWinningMove : Model -> Move -> Bool
nextPlayerHasNoWinningMove model move =
let
potentialModel =
applyMove model move
potentialFutureMoves =
getMoves White model.rack potentialModel.board
in
case Extras.find (userWinningMove potentialModel) potentialFutureMoves of
Just _ ->
False
Nothing ->
True
nextPlayerHasNoScoreIncreasingMove : Model -> Move -> Bool
nextPlayerHasNoScoreIncreasingMove model move =
let
potentialModel =
applyMove model move
potentialFutureMoves =
getMoves White model.rack potentialModel.board
in
case Extras.find (userScoreIncreasingMove 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
cpuScoreIncreasingMove : Model -> Move -> Bool
cpuScoreIncreasingMove =
isScoreIncreasingMove Red
userScoreIncreasingMove : Model -> Move -> Bool
userScoreIncreasingMove =
isScoreIncreasingMove White
isScoreIncreasingMove ball model move =
let
oldScore =
Model.currentScore ball model.board
newScore =
Model.currentScore ball (applyMove model move).board
in
newScore > oldScore
cpuTurn : Model -> Model
cpuTurn model =
let
moves : List Move
moves =
getMoves Red model.rack model.board
postMovementModel =
Extras.find (cpuWinningMove model) moves
|> Extras.orElseLazy (\() -> Extras.find (cpuScoreIncreasingMove model) moves)
|> Extras.orElseLazy (\() -> Extras.find (nextPlayerHasNoScoreIncreasingMove model) moves)
|> Extras.orElseLazy (\() -> Extras.find (nextPlayerHasNoWinningMove model) moves)
|> Extras.orElseLazy (\() -> Random.step (Random.sample moves) (Random.initialSeed 42) |> fst)
|> Maybe.map (applyMove model)
|> Maybe.withDefault model
postRackUpdateModel =
{ postMovementModel | rack = removeFromRack Red postMovementModel.rack }
in
if isCPULosingModel postRackUpdateModel then
{ postRackUpdateModel | gameState = Win }
else if isUserLosingModel postRackUpdateModel then
{ postRackUpdateModel | gameState = Loss }
else if isTieModel postRackUpdateModel then
{ postRackUpdateModel | gameState = Tie }
else
postRackUpdateModel
applyMove : Model -> Move -> Model
applyMove model ( ball, pinId ) =
{ model | board = Model.place ball pinId model.board }