Generating with constraints
So, the next step, actually generating things. As I wrote last week, my strategy first was to generate entirely random hands. But! I realized that if we do that, we make some assumptions about our partner, because they will have an average hand. I then wanted to generate a random hand for my partner given a certain constraint, in this case that our partner has 0 points. This way, we get the best bid for just our hand. Also, in a later stage I will want to generate hands with other constraints, for the next step of our bidding system.
My first solution was to use the fits function I already wrote. Awesome, code reuse to the max, right? The fits function, if you don’t remember, checks if a certain hand complies to a condition:
let rec fits (cards : Hand) (c : Condition) : bool = match c with | NCards(min, max, s) -> cardsofSuitinHand cards s >= min && cardsofSuitinHand cards s <= max | NPoints(min, max) -> pointsinHand cards >= min && pointsinHand cards <= max | And(p, q) -> fits cards p && fits cards q | Or(p, q) -> fits cards p || fits cards q
So, you give fits a hand and a condition, and it returns a boolean whether the hand satisfies the condition. We can use that, by just generating a random hand until it matches:
//this is an example of a hand constraint let constreent = fun x -> Desi.fits (Desi.Hand(x)) (Desi.points 0 12) *
* if you are wondering why my variable is called ‘constreent’ that is because constraint is reserved for future use.
We can now just generate random hands until we meet the condition:
let randomHandplusRemainderwithConstraint cardSet (constreent: Desi.Card List -> bool) = let mutable continueLooping = true let oneHand = oneRandomHand cardSet while continueLooping do let oneHand = oneRandomHand cardSet // Generate a random hand if constreent oneHand then continueLooping (oneHand , except cardSet oneHand)
But again, that is very very slow… 🙁 Since in the first situation, I just needed hands with no points, I could solve it with a constraint on individual cards, like so:
let randomHandplusRemainderwithCardConstraint cardSet (constreent: Desi.Card -> bool) = //get all the cards that fit the card constraint: let fitting = List.filter (constreent) cardSet //take 13 random from those: let oneHand = oneRandomHand fitting (oneHand , except cardSet oneHand)
Calling it with a constreent on cards:
let constreent = fun x -> Desi.cardtoPoint x <= 0 let (hand3, remainder) = randomHandplusRemainderwithCardConstraint remainder1 constreent
Finally, I made a relatively elegant and performant solution. The idea is to use conditions instead of functions on hands, and recursively break them down:
let rec randomHandwithCondition cardSet stillToTake (c: Desi.Condition) = match stillToTake with | 0 -> [] | n -> let oneCard = oneRandomCard cardSet oneCard :: (randomHandwithCondition cardSet (n-1) (updateCondition c oneCard ))
The updateCondition function takes a card and a condition and makes it smaller
let updateCondition condition card = match condition with | Desi.NPoints(min,max) -> let x = Desi.cardtoPoint card Desi.NPoints(min-x,max-x) | Desi.NCards(min, max, s) -> if helper.getSuit card = s then Desi.NCards(min-1, max-1, s) else Desi.NCards(min, max, s)