-
Notifications
You must be signed in to change notification settings - Fork 0
/
Engine.hs
74 lines (61 loc) · 3.3 KB
/
Engine.hs
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
{-
Engine.hs
Scott Bouloutian
This module defines the game logic behind Ultimate Tic-Tac-Toe
-}
module Engine(validMove,performMove,winner,rowFromSector,colFromSector,diagFromSector)
where
import Board
import Data.List
-- Determines whether a move on the given board is valid by checking the following criteria
-- 1. The big index is in the bounds
-- 2. The small index is in the bounds
-- 3. The space is empty
-- 4. The sector has not yet been won
-- 5. The big index corresponds to the small index of the last move unless the sector of the big index is already won
validMove :: (Int,Int) -> Board -> Bool
validMove (m1,m2) board = inBounds m1
&& inBounds m2
&& state board !! getIndex (m1,m2) == '_'
&& (sectorUtility $ getSector m1 board) == '_'
&& (n == -1 || m1 == n || (sectorUtility $ getSector n board) /= '_')
where n = snd $ lastMove board
-- Checks if an index is within its proper bounds
inBounds :: Int -> Bool
inBounds n = n>=0 && n<9
-- Performs the given move on the given board and returns the resulting board
performMove :: (Int,Int) -> Char -> Board -> Board
performMove move token board = Board {state = left ++ [token] ++ right, lastMove = move}
where left = fst split
right = tail (snd split)
split = splitAt (getIndex move) (state board)
-- Checks the board to see if the game is over and returns the winner
winner :: Board -> Char
winner board = sectorUtility [sectorUtility (getSector x board) | x <- [0..8]]
-- Looks at an array of characters (conceptualized as a 3x3 Tic-Tac-Toe board) and returns the winner
sectorUtility :: [Char] -> Char
sectorUtility sector | sectorWin sector 'X' = 'X'
| sectorWin sector 'O' = 'O'
| sectorIsFull sector = 'T'
| otherwise = '_'
-- Returns true if there are nto any empty spaces in the corresponding sector
sectorIsFull :: [Char] -> Bool
sectorIsFull sector = not (any ((==) '_') sector)
-- Returns true if someone has won the corresponding sector
sectorWin :: Eq a => [a] -> a -> Bool
sectorWin sector token= any ((==) True) ([allEqual token (rowFromSector x sector) | x <- [0,1,2]]
++ [allEqual token (colFromSector x sector) | x <- [0,1,2]]
++ [allEqual token (diagFromSector x sector) | x <- [0,1]])
-- Returns true is all the tokens in a given list are equal to the given token
allEqual :: Eq a => a -> [a] -> Bool
allEqual token list = all ((==) token) list
-- Retuurns a specific row given a sector
rowFromSector :: Int -> [a] -> [a]
rowFromSector row sector = [fst x | x <- zipWithIndex(sector), quot (snd x) 3 == row]
-- Returns a specific column given a sector
colFromSector :: Int -> [a] -> [a]
colFromSector col sector = [fst x | x <- zipWithIndex(sector), mod (snd x) 3 == col]
-- Returns a specific diagonal given a sector
diagFromSector :: Int -> [a] -> [a]
diagFromSector 0 sector = [fst x | x <- zipWithIndex(sector), y <- [0,4,8], snd x == y]
diagFromSector 1 sector = [fst x | x <- zipWithIndex(sector), y <- [2,4,6], snd x == y]