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)