-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathTicTacToeModel.elm
136 lines (100 loc) · 3.49 KB
/
TicTacToeModel.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
module TicTacToeModel where
import List exposing ((::), all, drop, filter, head, isEmpty, length, map)
import Maybe exposing (withDefault)
type Player = O | X
type Result = Draw | Winner Player
type alias Field = { col: Int, row: Int }
type alias Move = (Field,Player)
type alias Moves = List Move
type GameState =
FinishedGame Result Moves
| NotFinishedGame Player Moves
other : Player -> Player
other player =
case player of
X -> O
O -> X
moves : GameState -> Moves
moves state =
case state of
(NotFinishedGame _ moves) -> moves
(FinishedGame _ moves) -> moves
initialState : GameState
initialState = NotFinishedGame X []
isFieldEmpty : Moves -> Field -> Bool
isFieldEmpty moves field = all (\move -> not (fst move == field)) moves
subsequences : List a -> List (List a)
subsequences lst =
case lst of
[] -> [[]]
h::t -> let st = subsequences t
in
st ++ map (\x -> h::x) st
playerWon : Player -> Moves -> Bool
playerWon player =
let fieldsAreInLine fields =
all (\{col} -> col == 1) fields ||
all (\{col} -> col == 2) fields ||
all (\{col} -> col == 3) fields ||
all (\{row} -> row == 1) fields ||
all (\{row} -> row == 2) fields ||
all (\{row} -> row == 3) fields ||
all (\{col,row} -> col == row) fields ||
all (\{col,row} -> col + row == 4) fields
in subsequences
>> filter (\x -> length x == 3)
>> filter (all (\(_,p) -> p == player))
>> map (map fst)
>> filter fieldsAreInLine
>> isEmpty
>> not
addMove : Move -> GameState -> GameState
addMove move state =
let newMoves = move :: (moves state)
player = snd move
in
if | playerWon player newMoves -> FinishedGame (Winner player) newMoves
| length newMoves == 9 -> FinishedGame Draw newMoves
| otherwise -> NotFinishedGame (other player) newMoves
makeComputerMove : GameState -> GameState
makeComputerMove state = case state of
FinishedGame _ _ -> state
NotFinishedGame player moves ->
let fields = [
{col=2,row=2},
{col=1,row=1},
{col=3,row=3},
{col=1,row=3},
{col=3,row=1},
{col=1,row=2},
{col=2,row=1},
{col=2,row=3},
{col=3,row=2}
]
newField = filter (isFieldEmpty moves) fields |> head |> withDefault {col=0,row=0}
newMoves = (newField, player) :: moves
in
addMove (newField, player) state
makeHumanAndComputerMove : Field -> GameState -> GameState
makeHumanAndComputerMove field state =
case state of
FinishedGame _ _ -> state
NotFinishedGame player moves ->
if isFieldEmpty moves field
then addMove (field,player) state |> makeComputerMove
else state
undoMoves : GameState -> GameState
undoMoves state =
case state of
NotFinishedGame _ [] -> state
NotFinishedGame player moves ->
NotFinishedGame player (moves |> drop 2)
FinishedGame _ _ -> state
processClick : (Int,Int) -> GameState -> GameState
processClick (x,y) =
let col = 1 + x // 100
row = 1 + y // 100
in
if col >= 1 && col <= 3 && row >= 1 && row <= 3
then makeHumanAndComputerMove {col=col,row=row}
else identity