Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add State of Tic-Tac-Toe exercise #1248

Merged
merged 3 commits into from
May 23, 2024

Conversation

tofische
Copy link
Contributor

@tofische tofische commented May 7, 2024

Added a new Haskell practice exercise State of Tic-Tac-Toe based on the available problem specification.

Copy link

github-actions bot commented May 7, 2024

Hello. Thanks for opening a PR on Exercism 🙂

We ask that all changes to Exercism are discussed on our Community Forum before being opened on GitHub. To enforce this, we automatically close all PRs that are submitted. That doesn't mean your PR is rejected but that we want the initial discussion about it to happen on our forum where a wide range of key contributors across the Exercism ecosystem can weigh in.

You can use this link to copy this into a new topic on the forum. If we decide the PR is appropriate, we'll reopen it and continue with it, so please don't delete your local branch.

If you're interested in learning more about this auto-responder, please read this blog post.


Note: If this PR has been pre-approved, please link back to this PR on the forum thread and a maintainer or staff member will reopen it.

@MatthijsBlom
Copy link
Contributor

I don't get why the canonical data distinguishes between error causes. For many 'impossible' board states it is impossible to, from the board state alone, deduce how it was arrived at. Indeed, even of 'possible' board states it is impossible in general to know that they were arrived at legally.

Something like

data GameState
  = Ongoing { nextTurn :: Player }
  | Win { winner :: Player }
  | Draw
  | Impossible

would have made more sense to me.

module StateOfTicTacToe (gameState, GameError(..), GameState(..)) where

data GameError = WrongTurnO | WrongTurnX | KeptPlaying deriving (Eq, Show)
data GameState = Win | Draw | List | Ongoing | Error GameError deriving (Eq, Show)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That List isn't supposed to be there.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tofische
Copy link
Contributor Author

I don't get why the canonical data distinguishes between error causes. For many 'impossible' board states it is impossible to, from the board state alone, deduce how it was arrived at. Indeed, even of 'possible' board states it is impossible in general to know that they were arrived at legally.

Something like

data GameState
  = Ongoing { nextTurn :: Player }
  | Win { winner :: Player }
  | Draw
  | Impossible

would have made more sense to me.

I disagree, knowing the principal error cause is always beneficial, even if there might be situations in which it cannot be unambiguously determined.

You another suggestion is more interesting, I'd rather use something like ... Win | Lost ... instead of parameter (and the same for the next turn), but this is just a matter of style. The question is however, if these are then still the test cases from the problem specification / canonical data.

To be honest, I don't understand the role of the problem specification - does it serve more purpose than just a template for an exercise? To which extend am I allowed to deviate from it?

@ErikSchierboom
Copy link
Member

To be honest, I don't understand the role of the problem specification - does it serve more purpose than just a template for an exercise? To which extend am I allowed to deviate from it?

You're allowed to deviate from it when it makes sense for the language. So if the canonical data need to be tweaked because that would make the exercise better fit the track, then go for it.

@ErikSchierboom
Copy link
Member

What about:

data GameState = Win | Loss | Draw | Ongoing | WrongTurn | KeptPlaying deriving (Eq, Show)

@MatthijsBlom
Copy link
Contributor

MatthijsBlom commented May 22, 2024

I disagree, knowing the principal error cause is always beneficial, even if there might be situations in which it cannot be unambiguously determined.

Sure, knowing the principal error cause would be beneficial, it's just that it is impossible.

Even if knowable error causes existed, determining which one is the principal error cause would be subjective. Example: two Os and one X on the board. Did the wrong player start, or did the same player place a piece twice in a row? Or both?

However, knowable error causes do not even exist. In the example above, pieces could have changed color, and/or have been teleported, and/or have been removed after having been placed before. Who knows!

At the end of the day, whether a game state is reachable is determined entirely by there existing a route to it or not. If a game state is reachable, then you can prove it (provide a legal progression of game states that ends at the given state), and if it is not reachable then you cannot.

Now if you'd really like to be able to point out reachability via illegal moves, then you could define an extension of the set of legal moves and designate the difference as the possible illegal moves. I do not know how to do this properly. It is very easy to undershoot (some impossible states remain unexplainable even with illegal moves) or overshoot (impossible states are reachable in several ways).

You another suggestion is more interesting,

I included these fields because it seems like the Haskellish thing to do: do not do a bunch of work, only to throw part of the result away and to force your caller to redo your work to find out what you already knew.

gameState Ooh, I spot a Winner!
You Who is it?
gameState Go figure it out yourself.

I will not object strongly to not including these fields.

The question is however, if these are then still the test cases from the problem specification / canonical data.

I'd be fine with keeping the test cases and simply extending the expected output.


What about:

data GameState = Win | Loss | Draw | Ongoing | WrongTurn | KeptPlaying deriving (Eq, Show)

As explained above, I don't think it makes sense to distinguish between WrongTurn and KeptPlaying, or basically any error causes unless it is explicitly and narrowly defined which illegal moves are possible.

@ErikSchierboom ErikSchierboom merged commit 3587421 into exercism:main May 23, 2024
3 checks passed
@ErikSchierboom
Copy link
Member

Thanks @tofische!

@tofische tofische deleted the state-of-tic-tac-toe branch May 23, 2024 18:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants