Last active
February 22, 2017 15:55
-
-
Save DataWraith/3b9c856cc8cf1ee732d792804b9665a9 to your computer and use it in GitHub Desktop.
Simple two-player Go-Moku game written in Elm https://elm-gomoku.neocities.org
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
{- | |
"dependencies": { | |
"eeue56/elm-flat-matrix": "3.0.2 <= v < 4.0.0", | |
"elm-lang/core": "5.1.1 <= v < 6.0.0", | |
"elm-lang/html": "2.0.0 <= v < 3.0.0", | |
"elm-lang/svg": "2.0.0 <= v < 3.0.0" | |
}, | |
"elm-version": "0.18.0 <= v < 0.19.0" | |
-} | |
import Array | |
import Html | |
import Html.Attributes | |
import Html.Events | |
import Svg exposing (g, line, circle) | |
import Svg.Attributes exposing (width, height, style, viewBox, x1, x2, y1, y2, cx, cy, r, visibility) | |
import Svg.Events exposing (onClick) | |
import Matrix exposing (Matrix, indexedMap, get, set, filter) | |
main : Program Never Model Msg | |
main = | |
Html.beginnerProgram | |
{ model = initialModel 15 | |
, update = update | |
, view = view | |
} | |
-- MODEL | |
type Player | |
= Empty | |
| Black | |
| White | |
type alias Model = | |
{ size : Int | |
, board : Matrix Player | |
, currentPlayer : Player | |
, winner : Player | |
} | |
initialModel : Int -> Model | |
initialModel size = | |
{ size = size | |
, board = Matrix.repeat size size Empty | |
, winner = Empty | |
, currentPlayer = Black | |
} | |
-- UPDATE | |
type Msg | |
= MakeMove Int Int | |
| Reset | |
-- Returns the four (x, y) coordinates adjacent to x, y in a line pointing in | |
-- direction (dx, dy). | |
chainIndices : Int -> Int -> Int -> Int -> List ( Int, Int ) | |
chainIndices x y dx dy = | |
[ ( x + 1 * dx, y + 1 * dy ) | |
, ( x + 2 * dx, y + 2 * dy ) | |
, ( x + 3 * dx, y + 3 * dy ) | |
, ( x + 4 * dx, y + 4 * dy ) | |
] | |
getChainLength : Player -> Matrix Player -> List ( Int, Int ) -> Int | |
getChainLength player board indices = | |
if List.isEmpty indices then | |
4 | |
else | |
let | |
x = | |
(Tuple.first (Maybe.withDefault ( 0, 0 ) (List.head indices))) | |
y = | |
(Tuple.second (Maybe.withDefault ( 0, 0 ) (List.head indices))) | |
p = | |
get x y board | |
in | |
if p == Just player then | |
getChainLength player board (Maybe.withDefault [] (List.tail indices)) | |
else | |
4 - (List.length indices) | |
checkWinner : Int -> Int -> Player -> Matrix Player -> Player | |
checkWinner x y p board = | |
let | |
ul = | |
getChainLength p board (chainIndices x y -1 -1) | |
u = | |
getChainLength p board (chainIndices x y 0 -1) | |
ur = | |
getChainLength p board (chainIndices x y 1 -1) | |
r = | |
getChainLength p board (chainIndices x y 1 0) | |
dr = | |
getChainLength p board (chainIndices x y 1 1) | |
d = | |
getChainLength p board (chainIndices x y 0 1) | |
dl = | |
getChainLength p board (chainIndices x y -1 1) | |
l = | |
getChainLength p board (chainIndices x y -1 0) | |
in | |
if ul + dr == 4 then | |
p | |
else if u + d == 4 then | |
p | |
else if ur + dl == 4 then | |
p | |
else if r + l == 4 then | |
p | |
else | |
Empty | |
update : Msg -> Model -> Model | |
update msg model = | |
case msg of | |
Reset -> | |
initialModel model.size | |
MakeMove x y -> | |
if model.currentPlayer == Empty then | |
model | |
else | |
let | |
curState = | |
get x y model.board | |
winner = | |
checkWinner x y model.currentPlayer model.board | |
boardFull = | |
Array.length (filter (\square -> square == Empty) model.board) == 0 | |
nextPlayer = | |
if (winner /= Empty) || boardFull then | |
Empty | |
else if model.currentPlayer == White then | |
Black | |
else | |
White | |
in | |
if curState /= Just Empty then | |
model | |
else | |
{ model | |
| board = set x y model.currentPlayer model.board | |
, currentPlayer = nextPlayer | |
, winner = winner | |
} | |
-- VIEW | |
boardSquare : Int -> Int -> Player -> Svg.Svg Msg | |
boardSquare x y square = | |
let | |
offsetx = | |
101 * x | |
offsety = | |
101 * y | |
center = | |
50 | |
dimension = | |
101 | |
in | |
g [] | |
[ line | |
[ x1 (toString (offsetx + center)) | |
, y1 (toString (offsety)) | |
, x2 (toString (offsetx + center)) | |
, y2 (toString (offsety + dimension)) | |
, style "stroke:rgb(128,128,128);stroke-width:2" | |
] | |
[] | |
, line | |
[ x1 (toString (offsetx)) | |
, y1 (toString (offsety + center)) | |
, x2 (toString (offsetx + dimension)) | |
, y2 (toString (offsety + center)) | |
, style "stroke:rgb(128,128,128);stroke-width:2" | |
] | |
[] | |
, if square == Black then | |
circle | |
[ cx (toString (offsetx + center)) | |
, cy (toString (offsety + center)) | |
, r "45" | |
, style "fill:rgb(0, 0, 0);" | |
] | |
[] | |
else if square == White then | |
circle | |
[ cx (toString (offsetx + center)) | |
, cy (toString (offsety + center)) | |
, r "45" | |
, style "fill:rgb(255,255,255);" | |
] | |
[] | |
else | |
circle | |
[ cx (toString (offsetx + center)) | |
, cy (toString (offsety + center)) | |
, r "45" | |
, visibility "hidden" | |
, Svg.Attributes.pointerEvents "all" | |
, onClick (MakeMove x y) | |
] | |
[] | |
] | |
playerToString : Player -> String | |
playerToString player = | |
if player == Empty then | |
"Neither" | |
else if player == Black then | |
"Black" | |
else | |
"White" | |
centerAlign : Html.Attribute Msg | |
centerAlign = | |
Html.Attributes.style | |
[ ( "width", "50%" ) | |
, ( "margin-left", "auto" ) | |
, ( "margin-right", "auto" ) | |
] | |
view : Model -> Html.Html Msg | |
view model = | |
let | |
dimension = | |
model.size * 101 | |
in | |
Html.div [ centerAlign ] | |
[ Html.h1 [] [ Html.text "Go-Moku" ] | |
, Html.p [] [ Html.text "Connect exactly 5 stones horizontally, vertically or diagonally" ] | |
, Html.p [] [ Html.text ("Winner: " ++ (playerToString model.winner)) ] | |
, Html.p [] [ Html.text ("To Move: " ++ (playerToString model.currentPlayer)) ] | |
, Svg.svg | |
[ width "600px" | |
, height "600px" | |
, viewBox ("0 0 " ++ (toString dimension) ++ " " ++ (toString dimension)) | |
] | |
[ Svg.rect | |
[ width (toString dimension) | |
, height (toString dimension) | |
, style "fill:rgb(251,234,173);" | |
] | |
[] | |
, g [] (Array.toList (indexedMap boardSquare model.board).data) | |
] | |
, Html.button [ Html.Events.onClick Reset ] [ Html.text "Reset" ] | |
] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment