Created
May 22, 2016 06:40
-
-
Save johnomarkid/b2a0eece875f5f7f725a3482ed0b05e3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module GameLogic exposing (..) | |
import Model exposing (..) | |
-- Helpers | |
createScoreboard : Mark -> Board -> List (List Int) | |
createScoreboard mark board = | |
(List.map2 | |
(\row items -> | |
List.map2 | |
(\col item -> | |
if item == mark then | |
1 | |
else | |
0 | |
) | |
[0..((List.length board) - 1)] | |
items | |
) | |
[0..((List.length board) - 1)] | |
board | |
) | |
listToStatus : List Int -> Status | |
listToStatus items = | |
if (List.sum items) == (List.length items) then | |
Win | |
else | |
Ongoing | |
get : Int -> List a -> a | |
get n xs = | |
List.head (List.drop n xs) | |
|> fromJust | |
fromJust : Maybe a -> a | |
fromJust x = | |
case x of | |
Just y -> | |
y | |
Nothing -> | |
Debug.crash "error: fromJust Nothing" | |
invertScoreboard : List (List a) -> List (List a) | |
invertScoreboard board = | |
List.map (\i -> List.map (\row -> get i row) board) [0..((List.length board) - 1)] | |
invertUpVertical : List (List number) -> List number | |
invertUpVertical board = | |
let | |
boardLength = | |
((List.length board) - 1) | |
in | |
List.map2 | |
(\row index -> | |
if (get (boardLength - index) row) == 1 then | |
1 | |
else | |
0 | |
) | |
board | |
[0..boardLength] | |
invertDownVertical : List (List number) -> List number | |
invertDownVertical board = | |
List.map2 | |
(\row index -> | |
if (get (0 + index) row) == 1 then | |
1 | |
else | |
0 | |
) | |
board | |
[0..((List.length board) - 1)] | |
-- randLocation : Model -> ( Location, Model ) | |
-- randLocation model = | |
-- let | |
-- randRange = | |
-- int 0 ((List.length model.board) - 1) | |
-- | |
-- rowResult = | |
-- generate randRange model.randSeed | |
-- | |
-- colResult = | |
-- generate randRange (snd rowResult) | |
-- | |
-- -- use new seed from rowResult | |
-- location = | |
-- ( (fst rowResult), (fst colResult) ) | |
-- | |
-- newSeed = | |
-- snd colResult | |
-- | |
-- newModel = | |
-- { model | randSeed = newSeed } | |
-- | |
-- markAtLocation = | |
-- get (fst location) model.board | |
-- |> get (snd location) | |
-- in | |
-- if markAtLocation == NA then | |
-- ( location, newModel ) | |
-- else | |
-- randLocation newModel | |
-- Core Gameplay | |
checkWin : Model -> Model | |
checkWin model = | |
let | |
-- if sum of row = 3 for items that match mark | |
scoreBoard = | |
createScoreboard model.turn model.board | |
checkRow = | |
scoreBoard | |
|> List.map listToStatus | |
checkCol = | |
scoreBoard | |
|> invertScoreboard | |
|> List.map listToStatus | |
checkUpVertical = | |
scoreBoard | |
|> invertUpVertical | |
|> listToStatus | |
checkDownVertical = | |
scoreBoard | |
|> invertDownVertical | |
|> listToStatus | |
status = | |
List.append checkRow checkCol | |
|> (\v -> checkUpVertical :: v) | |
|> (\v -> checkDownVertical :: v) | |
|> List.member Win | |
|> (\v -> | |
(if v == True then | |
Win | |
else | |
Ongoing | |
) | |
) | |
-- tie if moves = 0 and no win above | |
remainingMoves = | |
createScoreboard NA model.board | |
|> List.map List.sum | |
|> List.sum | |
newStatus = | |
case remainingMoves == 0 of | |
True -> | |
if status == Ongoing then | |
Tie | |
else | |
status | |
False -> | |
status | |
in | |
{ model | status = newStatus } | |
updateSquare : Location -> Model -> Model | |
updateSquare location model = | |
let | |
newBoard = | |
(List.map2 | |
(\x row -> | |
List.map2 | |
(\y item -> | |
if ( x, y ) == location then | |
model.turn | |
else | |
item | |
) | |
[0..model.boardSize] | |
row | |
) | |
[0..model.boardSize] | |
model.board | |
) | |
in | |
{ model | board = newBoard } | |
changeTurn : Model -> Model | |
changeTurn model = | |
let | |
newTurn = | |
case model.turn of | |
X -> | |
O | |
O -> | |
X | |
NA -> | |
NA | |
in | |
{ model | turn = newTurn } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Main exposing (..) | |
import Html.App as Html | |
import Html exposing (..) | |
import Html.Events exposing (onClick) | |
import Html.Attributes exposing (..) | |
import Model exposing (..) | |
import GameLogic exposing (updateSquare, checkWin, changeTurn) | |
-- Helper | |
markToString : Mark -> String | |
markToString mark = | |
case mark of | |
X -> | |
"X" | |
O -> | |
"O" | |
NA -> | |
"" | |
--Update | |
type Msg | |
= NoOp | |
| Reset | |
| IncreaseBoard | |
| Turn Location | |
update : Msg -> Model -> Model | |
update msg model = | |
case msg of | |
NoOp -> | |
model | |
Reset -> | |
initialModel 2 | |
IncreaseBoard -> | |
initialModel (model.boardSize + 1) | |
Turn location -> | |
let | |
newModel = | |
updateSquare location model | |
|> checkWin | |
|> (\aModel -> changeTurn aModel) | |
|> (\bModel -> | |
-- this is a recursive call to update for the cpu move if turn == O | |
-- can't call this if someone has won because causes infinite loop | |
if bModel.status /= Ongoing then | |
changeTurn bModel | |
else | |
(if bModel.turn == O then | |
-- let | |
-- ( loc, newModel ) = | |
-- randLocation bModel | |
-- in | |
--update (Turn loc) newModel | |
bModel -- temporary | |
else | |
bModel | |
) | |
) | |
in | |
newModel | |
-- View | |
squareView : Int -> Int -> Mark -> Html Msg | |
squareView rowNum colNum mark = | |
let | |
location = | |
( rowNum, colNum ) | |
action = | |
if mark == NA then | |
(Turn location) | |
else | |
NoOp | |
in | |
div [ onClick action | |
, style [("width", "50px"), ("height", "50px"), ("background-color", "gray"), ("border", "1px solid black"), ("justify-content", "center"), ("align-items", "center") ,("display", "flex")]] | |
[text (markToString mark)] | |
rowView : Int -> Int -> List Mark -> Html Msg | |
rowView bs rowNum items = | |
let | |
squares = | |
List.map2 (squareView rowNum) [0..bs] items | |
in | |
div [style [("display", "flex")]] squares | |
view : Model -> Html Msg | |
view model = | |
let | |
board = | |
if model.status == Ongoing then | |
div [] | |
(List.map2 (rowView model.boardSize) [0..model.boardSize] model.board) | |
else | |
div [] [text "Play again!"] | |
buttons = | |
div [] [ | |
button [onClick Reset] [text "Reset Game"] | |
, button [onClick IncreaseBoard] [text "Increase Board Size"]] | |
statusMessage = | |
(case model.status of | |
Win -> | |
(markToString model.turn) ++ " Wins!" | |
Tie -> | |
"It's a Tie!" | |
Ongoing -> | |
"Game is ongoing." | |
) | |
in | |
div [] [board, buttons, div [] [text statusMessage]] | |
main: Program Never | |
main = | |
Html.beginnerProgram { model = (initialModel 2), view = view, update = update } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Model exposing (..) | |
type alias Model = | |
{ board : Board | |
, turn : Mark | |
, status : Status | |
, boardSize : Int | |
} | |
initialModel : Int -> Model | |
initialModel bs = | |
{ board = initialBoard bs | |
, turn = X | |
, status = Ongoing | |
, boardSize = bs | |
} | |
type alias Board = | |
List (List Mark) | |
initialBoard : Int -> Board | |
initialBoard numRows = | |
let | |
returnNum _ = | |
NA | |
in | |
List.map (\i -> List.map returnNum [0..numRows]) [0..numRows] | |
type Status | |
= Ongoing | |
| Tie | |
| Win | |
type Mark | |
= X | |
| O | |
| NA | |
type alias Location = | |
( Int, Int ) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment