(Building Tic Tac Toe with Middle Out Design)
Introduction
In the middle out programming article I explained how a small language can describe your problem space. Here we apply that idea to Tic‑Tac‑Toe using Elm. The DSL expresses a game as data, separating the rules from any interface.
Primitives
The language starts with simple data types. They model only the states that can actually occur so impossible boards are impossible to represent.
`elm type Player = X | O
type Cell = Empty | Mark Player
type alias Position =
-- Exactly three rows and three columns type alias Board = Array (Array Cell)
`
Cell
can only be Empty
or contain a player mark. Board
is always three‑by‑three, ensuring we never hold illegal sizes.
Operations
Operations describe how a board evolves.
`elm emptyBoard : ( Board, Player ) emptyBoard = ( Array.repeat 3 (Array.repeat 3 Empty), X )
place : Position -> ( Board, Player ) -> ( Board, Player ) place pos ( board, current ) = case Array.get pos.row board |> Maybe.andThen (Array.get pos.col) of Just Empty -> let updatedRow = Array.get pos.row board |> Maybe.map (Array.set pos.col (Mark current)) |> Maybe.withDefault (Array.empty)
newBoard = Array.set pos.row updatedRow board
next = if current == X then O else X
in
( newBoard, next )
_ ->
( board, current )
```
These operations guarantee we never overwrite a filled cell and always alternate players.
Evaluator
An evaluator interprets a list of moves.
elm play : List Position -> ( Board, Player ) -> ( Board, Player ) play moves game = List.foldl place game moves
This DSL lets us describe an entire match purely as data.
Playing a Game
`elm exampleMoves : List Position exampleMoves = [ , , , , ]
finalBoard : ( Board, Player ) finalBoard = play exampleMoves emptyBoard `
In an Elm application you could call play
from the update
function and render the result in the view
. Because the game logic lives in a small language, any interface—from command line to web UI—can reuse it.
Conclusion
By designing a language that expresses Tic‑Tac‑Toe, we separate game rules from presentation. Elm's types remove impossible states and the evaluator interprets a sequence of moves. The same middle‑out approach scales to more complex domains.