So,
here I am, after years of procedural and oop programming, trying to learn Elm.
I started yesterday, so please be kind with my absolute beginner question.
After reading the guide until the “Command and subscriptions” section, I decided to try and implement a very simple tic tac toe.
What could go wrong, I tought… but now I am stuck, so a feedback would be greatly appreciated (again, please be kind :))
I am at the point where I have to set the turn, after the user clicks a cell.
Here is the code:
now you are in the position where upon message Turn n you will want to set the nth board cell to either X or O. I think that you are on the wrong path with handling Turn 0 to Turn 8 in your update function seperately. Rather I think you will want to handle Turn n and update the n-th list element of model.board. You will also want to toggle board.currentCell whenever you are receiving a Turn n message.
So you are faced with updating the nth element of a list. I suggest mapping over all elements of the list, and only really changing the nth element. Since you do not have access to the element’s index when using List.map, you should be using List.indexedMap which gives you the element as well as the element’s index.
I added a check to see if the move is actually possible, otherwise you could overwrite the cell value,
the check I added (during a coffee break):
update : Msg -> Model -> Model
update msg model =
case msg of
Turn n ->
if cellByIndex model.board n == Just Empty then
{ model
| board =
List.indexedMap
(\i prev ->
if n == i then
model.currentCell
else
prev
)
model.board
, currentCell =
if model.currentCell == X then
O
else
X
}
else
model
you could check for a winner after every Turn n message. Suppose you have a function isGameWon : Board -> Bool, you could add a field isWon : Bool to your model and you could update that field every tiem your process a Turn n. You will have to be careful to call isGameWon on updatedBoard instead of model.board so that your change to the board is reflected. The following code illustrates how you can use a let statement to do that:
case msg of
Turn n ->
let
updatedBoard = … -- List.indexedMap etc. like before
in
{ model | board = updatedBoard, isWon = isGameWon updatedBoard }
If model.isWon is True, you could display a “Game over” label in your UI somewhere.
To check if a game is won, you will probably want to implement that using a long boolean expression, since there are only so many combinations of cell1 == cell2 && cell2 == cell 3 for you to check.
@carloratm I would have expected to find it in the Core Language section of the guide, but I am having trouble finding an introduction to the let statement in the guide either.
Quickly checking through the guide, first time it’s shown seems to be in Commands and Subscriptions > Time. Most examples in the guide are probably just so simple that they don’t really need to use let ... in.
gameState board c =
case Array.toList board of
c0 :: c1 :: c2 :: c3 :: c4 :: c5 :: c6 :: c7 :: c8 ->
if c0 == c && c1 == c && c2 == c || c3 == c && c4 == c && c5 == c || c6 == c && c7 == c && c8 == c || c0 == c && c4 == c && c8 == c || c2 == c && c4 == c && c6 == c || c0 == c && c3 == c && c6 == c || c1 == c && c4 == c && c7 == c || c2 == c && c5 == c && c8 == c then
c
else
Nothing
_ ->
Nothing
but I was getting error
I am inferring a weird self-referential type for `c0`
Deployed here: https://2pxsolidblack.gitlab.io/elm-tac-toe/
I have done that not looking at your (surely amazing) implementations, so please forgive any elm heresy I might have written.
Yes. I know. I need to fix the css. It is not mobile friendly at all. It works on a desktop. It’s a simple fix I just need to do it. Like a 10 min redo of the layout.
It’s broken on my android as well.
The cells on the right are wrapping to the next line.