0
  • I'm building a game (kind of poker) in haskell but i'm new to haskell and generally get stuck when try to think how to implement a thing in purely functional language.*

Game is of 3 players(Just to make it easy to explain). Two players will play from the table cards and one will play from the stack. Both kinds of players will take one card and use it if useful(replace one of your card in hand) or just dump it(if not). Winning is just full-house or a straight.

The Two of them have some understanding, something they agree before to help each other. Like if i took K (lets say) from table and next hand i took a 4 from the table means i'm going for a full house and hence you should help if you can in any possible way. Each of these player should help each other so that one of them should win before the player playing with the stack but not at the cost of oneself.

Table has total 5 cards and so does each player

Implement Understanding between two players :

I'm stuck how to track what other player have took from the table. I'm trying to do that by adding the cards to my hand itself (Virtually remembering the thing) and while dong calculations of my hand (real) i'm going to take only first five of cards and while doing calculation for helping him i'm going to drop five cards and then do calculations with that. Is there any better strategy to do the same?? It might be trivial but i'm amateur in world of haskell. If you want code I can just put it up.

basic set up of my Game :

data Card = Card {
    c :: (Int, Char) 
   } deriving (Eq, Ord, Show)

data Deck = Deck {
   d :: [Card]
} deriving (Show, Eq, Ord)  

data Game = Game {
  bact1Hand :: [Card],
  bact2Hand :: [Card],
  orgHand  :: [Card],
  tableCards :: [Card],
  deck :: Deck
} deriving (Show, Eq, Ord)  

type GameState a = State Game a`
Community
  • 1
  • 1
user2754673
  • 439
  • 3
  • 13
  • We don't have variables in Haskell! Use [recursion](http://learnyouahaskell.com/recursion). – AJF Oct 05 '15 at 06:20
  • At least that i know. But how you going to remember ? about the last hand or the card other player took from the table? thanks – user2754673 Oct 05 '15 at 06:23
  • 1
    Additional arguments to your function. – Zeta Oct 05 '15 at 06:24
  • Can you explain that a little. coz in lets say 5th run of the poker. I have to update the variable that was used to remember the thing. and i need all the preserved information of all the rounds happened before.And as much as i can understand, if you are not storing it in a variable, u'll need some notion of variable to do that. coz u are remembering. – user2754673 Oct 05 '15 at 06:27
  • 2
    Think in terms of game positions. A position conpletely determines the state of the game. You pass data that represents a position. A move transforms a position into another position. Start with defining a data type that is able to represent a position. – n. m. could be an AI Oct 05 '15 at 06:32
  • 3
    Yes, put up your code. – dfeuer Oct 05 '15 at 06:51
  • @AJFarmar, what do you mean we don't have variables? – dfeuer Oct 05 '15 at 06:53
  • @dfeuer As in mutable variables. Speaking for a newbie here. – AJF Oct 05 '15 at 06:56
  • it would be much easier to discuss and answer if you would give us a bit code to work with - for example the way you represent the cards, hands, table, ... – Random Dev Oct 05 '15 at 07:10
  • I'm trying to answer your question but I'm not sure what you're asking exactly. The title suggests you just want to know how to deal with mutable state. The code suggests you already know how to use the `State` monad. I'm not sure if you're asking about the reasoning behind the understanding (the player AI aspect), or what kind of data structure to use for the players' knowledge, or how to update the understanding data structure. Or do you need suggestions on how to update the game state and deal with players' moves? – Sam van Herwaarden Oct 05 '15 at 15:27

1 Answers1

1

If you want to keep it simple and are not ready for advanced Haskell things, you can use recursive transforms, as it was suggested in comments to you question.

The main idea is that you create a data type data Game that fully describes the state of the game; and then you write a function "player A makes action 1" that transforms the game (e.g. act:: Player -> Action -> Game -> Game). Such function transforms the full game state: it preserves the state from input to the output, but changes some parts of it, related to the player and the action (e.g. adding a card to hand). The last step is to bind the function to the IO actions: in the simplest setting you create a main cycle in which the Game changes, waiting for IO input on each iteration (e.g. just like OpenGL main cycle). Or you can use Control.Concurrent.MVar to pass IO actions asynchronously.

So, in short code:

-- | State of the AI
data Mind = Mind
    { overallLogic :: ???
    , perPlayerLogic :: [(PlayerID, Understanding)]
    }

-- | Undersanding of one player by another player
data Understanding = ???

newtype PlayerID = PlayerID Int

-- | Full state of the player
data Player = Player
    { id :: PlayerID
    , hand :: [Card]
    , mind :: Mind
    , ...
    }

-- | indication of the game state
data Status = PlayersTurn PlayerID | PlayerWon PlayerID

-- | Full state of the game
data Game = Game
    { status :: Status
    , players :: [Player] -- or consider (Int)Map of player-id
    , ...
    }

-- | Events in game are passed from IO: they indicate players' actions
data Event = PlayersTurn PlayerID Action ...
           | WhateverElseEvent

-- | the function to transform Game's state
react :: Event -> Game -> Game
react ev game = game'
    where game' = ... -- here is game logic 

And somewhere you put the main cycle in a separate thread:

mainLoop :: MVar Event -> Game -> IO ()
mainLoop mevent game = do
    event <- takeMVar mevent
    let game' = react event game
    if checkIfFinished game'
        then return ()
        else mainLoop mevent game'

Note the tail recursive call - the cycle goes until checkIfFinished returns True. checkIfFinished is your logic to decide whether to finish game or not, based on the Game state. takeMVar waits for another thread to put something. In other threads you add IO actions that get user input, make out of them the Event data, and put it using putMVar.

Update: thanks to comments, I put a bit off-topic part of the answer to the bottom as a "further reading".

If you want to remember states globally, GHC offers two types for you: Data.STRef and Control.Concurrent.MVar. However, this is not "purely functional" approach. You could read about statefull computations and the reader monad here http://learnyouahaskell.com/. Commentators suggest that the state monad is better than the reader monad for current purposes - thanks, I admit this. Anyway, learnyouahaskell is a great source for these kinds of information :) .

The most purely functional and useful in applications approach would be to use Functional Reactive Programming. There is a notion of behavior to represent a time-varying value. Some people would say FRP is an overkill here, and, probably, they would be right. But when you decide to add a GUI and other IO, using nice interfaces provided by e.g. reactive-banana will simplify your life significantly.

artem
  • 363
  • 1
  • 9
  • 1
    What does `Reader` have to do with it? – dfeuer Oct 05 '15 at 06:49
  • Passes the state of the game. The same recursion, but the data are passed implicitly via monad environment. – artem Oct 05 '15 at 06:58
  • the edit is better (but you mention the reader-monad while you describe the state-monad) - but still it's really hard to see how this apply to the question – Random Dev Oct 05 '15 at 06:58
  • 6
    I think FRP is quite thoroughly overkill for a poker game. – dfeuer Oct 05 '15 at 07:03
  • I don't see what is wrong in my answer. For example, citing the mtl library: The Reader monad (also called the Environment monad). Represents a computation, which can read values from a shared environment, pass values from function to function, and execute sub-computations in a modified environment. Using Reader monad for such computations is often clearer and easier than using the State monad. https://hackage.haskell.org/package/mtl-2.2.1/docs/Control-Monad-Reader.html – artem Oct 05 '15 at 07:04
  • 4
    no it's harder and it's useless in this case as you want the *state* to stay (not only be modified locally) - what you describe is useful for interpreters or app-configuration - but the *state-monad* is named that for a reason – Random Dev Oct 05 '15 at 07:06
  • Yes, I admit that FRP is overkill here, so I added simpler explanation. But I keep it for completeness. It is better to make people aware of what tools are available than always give. – artem Oct 05 '15 at 07:07
  • 3
    FRP here would be an implementation detail for the interaction - the question is far more basic: it's how to handle state in a pure setting – Random Dev Oct 05 '15 at 07:08
  • Ok, I wouldn't argue with that. Significantly updated the answer -- since I started it, I want finish it properly. – artem Oct 05 '15 at 08:22
  • now your gamestate is hidden in the main loop - how would the application be able to get a representation of the updated state? – Random Dev Oct 05 '15 at 08:31
  • also: if you want to go this way (having the events in some kind of handler-loop than you should use something like [`Control.Concurrent.Chan`](https://hackage.haskell.org/package/base-4.8.1.0/docs/Control-Concurrent-Chan.html#t:Chan) instead (IMO) – Random Dev Oct 05 '15 at 08:33
  • 2
    Well the question is a bit vague so you're not really to blame that it's hard to formulate a strong answer. But I think you're drifting off a bit also taking IO into the answer (the question seems to be about the logic inside the game, not so much about user interaction). Since the main question seems to be how to update variables in Haskell, I think the asker would benefit most from an explanation of how to use the `State` monad (since this is the go-to solution for this kind of problem). You clearly put quite some effort in the answer but it would benefit from being more to-the-point. – Sam van Herwaarden Oct 05 '15 at 08:33
  • Main loop is IO action, right? So one can easily add output action inside it – artem Oct 05 '15 at 08:34
  • well yes you could but then the abstraction is useless (I guess you would get the input right there too) – Random Dev Oct 05 '15 at 08:35
  • 1
    Ok, the better answer would be to explain State monad, I agree. But since it was not in my original answer, let others give it. I leave the answer as is now, because the office hours started already :) – artem Oct 05 '15 at 08:43
  • I thought that might be trivial, but looks lot of not. Thanks for helping me with code. – user2754673 Oct 05 '15 at 08:50