svarlet

svarlet

Hi all,

Let’s assume we are all coding a poker game as a purely functional library (=> no processes at this stage). If you don’t feel comfortable with poker, any other familiar multiplayer game will do.

How would you model it?
How would you design user interactions?
How would you validate user actions?

Thanks

Showing Posts 1 to 10

peerreynders

peerreynders

So at some point will there be processes? In that case, have you already had a look at The Erlangelist: To spawn, or not to spawn?. It looks at bridge for the purposes of the article - though as it uses processes it may not address your exact concerns (I also don’t recall any validations).

svarlet

svarlet OP

Yes, read it again this morning. It’s blackjack not bridge! :stuck_out_tongue:

It’s interesting but taking many shortcuts, notably regarding validations. I wonder how he would handle validation of user actions. In BlackJack, there are few possible moves and they are not complex, therefore there is not much to validate. This is my problem with his example.

Once you put your finger into verifying that every user action is possible and authorised, your code becomes a mess. For example, in a game where players select a card from their hand:

when Player P select card C in game G
→ has G started?
→ is G still playable (not game over) ?
→ does G have the min number of players?
→ does P own card C?
→ has P already selected a card before C?

That’s what just happened to me on a side project. Therefore I’m starting to think either:

  • separation of concern, another module should validate (accept or reject with a descriptive error) user actions;
  • defining a finite state machine;
  • not validate anything and let cheaters cheat, hackers hack, processes crash, and good people lose :smiley:

Of course, the second action is not acceptable.

kokolegorille

kokolegorille

peerreynders

peerreynders

Of course it is blackjack - serves me right for posting before my first coffee in the morning. :rolling_eyes:

Well, Saša Jurić already mentions :gen_statem and there is even the concept of communicating finite-state machines. However rather than coding the validation as an FSM straight away, it may make more sense to capture it in a state diagram first. With the states clearly identified a “purely functional implementation” could break the validation process into two phases:

  • State identification.
  • Validation based on identified state.
svarlet

svarlet OP

Interesting and thorough example but there it seems the author is using processes for everything and letting them crash when an unauthorised action happens.
Unfortunately, that is not what I’m curious about here.

kokolegorille

kokolegorille

The game logic is separated from genserver in the example

Qqwy

Qqwy

TypeCheck Core Team

So to first do note something about structuring with processes: In multiplayer games, there usually is something the player can interact with directly/frequently, and other things that the player only interacts with sporadically (or not at all).

For a game like poker, every table/round is nearly completely isolated: The dealt cards and player actions in one game do not at all matter for another game that is happening at the same time.


And now for the ṕurely functional library’:

One round of poker should definitely be its own, purely functional piece of logic. I think it works very well as a finite state machine. Building a finite state machine in a functional language is not that difficult (However, I personally think it is easier in a strongly typed functional language because you are forced to be more explicit in the different states, how/which transitions (can) happen, and that all states are handled when reading out something from the state machine).

The state machine should make sure that only actions that are allowed at this time can be performed; So ‘player 3’ is not allowed to bet/raise/call until ‘player 2’ has done so. Matching a ‘user’ to ‘player 3 in this round’ is something that should probably exist outside of the state machine, though.

I’d personally strongly advise to separate the state machine from the GenServer that runs it as a process. This both makes it easier to test the thing, as well as making it more explicit where the boundaries of logic vs. communications are.

I have not used :gen_statem before (But I did dabble with the older :gen_fsm which I did not particularly like), but I’d advise to just build the state machine and the transitioning logic myself, rather than using a leaky abstraction layer on top of it.

svarlet

svarlet OP

@Qqwy I agree with everything you said. This is where I would appreciate a type system with algebraic data types. Without it, I feel a bit overwhelmed by the number of repetitions in the production code (validate this, validate that, …), and by the number of tests to write as well. So for one bit of game logic, I end up writing tons of validation code. It is currently mixed in the actual logic and that looks messy. That’s why I thought of an FSM to reject obviously impossible actions (like starting a game when it has already started). However, it won’t help with finer grain issues like “player 3 is trying to play before player 2” or “player A is playing a card that he doesn’t own”.
Not sure I’m making progress here.

OvermindDL1

OvermindDL1

As a state blob that I pass around. I do exactly this in the languages I’ve implemented in Elixir, just passing around an env struct, threading it through every function.

Well in purely functional you cannot get user input other than before your code runs, so I’d do it via the repl with them calling the function with the current state along with their action as a kind of ‘event input’.

In reality you need some way to get user actions at runtime, a Haskell IO monad can do that functionally (by blackboxing using input and pretending it has been there since the start even if it has not) or via event passing between purely functional processes or so.

Depends entirely on what you want to validate, but you’d do it in that above process function.

This is not actually that hard. Given a language with Sum types (or tagged tuples on the BEAM) I’d just have each action be part of that sum type that they pass in with the state, and via simple pattern matching you only handle what is valid for a given state/event mix. With matchers it is trivial to just work on the valid actions and crash on the invalid (with perhaps a single wrapper to give pretty errors).

Likewise, I just implemented an FSM on gen_server anytime I needed a FSM, just by pattern matching states and such.

svarlet

svarlet OP

@OvermindDL1 so instead of drowning in validations, you would just code the happy path and crash for all players of this party?

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
Blokh
Hey guys, I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly Do you guys have any suggestions what is the best prac...
New
roeland
Kia ora, We have been using elixir-google-api to connect to Google Drive. However, with the updates to Tesla due to CVEs this is now bro...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
Onor.io
I have what I’ve heard referred to as a “lookup table” in my database. This is a way of assigning codes to common values. One common lo...
New
jaybe78
Hello, I’m developing a online persistent chat system (what’s app) like using elixir/dynamodb/aws for a mobile app(flutter). The diffic...
New
Trolleger
What approach to take when sending live updates to “random” users Hi! I have a question, I have a little chat app, and when I create a DM...
New

Other Trending Topics Top

garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
wintermeyer
There are three potential reasons for members of this forum to have a look at https://vutuv.de You are tired or annoyed of LinkedIn. Yo...
New
aseigo
ICal is a library for interacting with iCalendar data. It parses iCalendars into typed Elixir structs via ICal.from_ics, and can prepare ...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews