From 3e64db3c109011beb4285b59bbee40f573685e1c Mon Sep 17 00:00:00 2001
From: Nemo
Date: Sun, 14 Jun 2020 02:36:04 +0530
Subject: [PATCH] Adds Draw One support

README.md  7 ++++
drawone.mzn  19 +++++++++++++++++++
gameplay.mzn  44 +++++++++++++++++++++++
symbols.mzn  15 +++++++++++++++
4 files changed, 61 insertions(+), 24 deletions()
create mode 100644 drawone.mzn
create mode 100644 symbols.mzn
diff git a/README.md b/README.md
index 3ee2d47..1857877 100644
 a/README.md
+++ b/README.md
@@ 107,12 +107,13 @@ We subdivide the score into the following sections:
### Missing Constraints
 [ ] Symbol(■)  Draw One Card
 [ ] Symbol  Second card face up
 [ ] Symbol  Second card face down
+ [x] Symbol (■)  Draw One Card
+ [ ] Symbol (**=**)  Second card face up
+ [ ] Symbol (≂)  Second card face down
 [ ] Symbol (✠)  Simultaneous play
 [ ] Since the model misses out on the turndynamics, and treats symbols as global counters (instead of being attached to specific cards), there are some additional constraints that will be required. Without these, I'm expecting to see the same card being used for multiple symbols. Some sort of **Symbol counter** per round that keeps track of total number of cards you've claimed for symbols would be a better approach.
 [ ] **Additional Card Play**. Every round, all players can opt to play one extra card per artist they've already played. Note that this rule is very ambigously worded in the rules. I'm planning to implement the "blessed" variant. See https://boardgamegeek.com/thread/473713/playingadditionalcardsduringscoring for more details.
+ [ ] In case `DrawOne` card is the last card of a round, then `PlayableCards` for that round does not increase by 1, but only for the subsequent rounds. Since I'm maintaining awards as a boolean on the (Round,Artist) tuple, this becomes quite hard to model. Unless this gets violated in the winning entries, I don't plan to model this.
## TODO
diff git a/drawone.mzn b/drawone.mzn
new file mode 100644
index 0000000..ee587f5
 /dev/null
+++ b/drawone.mzn
@@ 0,0 +1,19 @@
+array[Rounds,Players,Artists] of var bool: DrawOneCard;
+
+% Total Playable Cards for any round are set equal to the Starting Hand
+array[Rounds,Players] of var int: PlayableCards;
+
+% Playable Cards in any round = Starting Cards + Draw One cards that give you an extra playable card
+constraint forall(p in Players, r in Rounds) (
+ PlayableCards[r,p] = StartingCardsInHand[r,p] + sum(a in Artists) (DrawOneCard[r,p,a])
+);
+
+% Every artist only has a single Draw One Card
+constraint forall(a in Artists) (
+ sum(p in Players, r in Rounds) (DrawOneCard[r,p,a]) = 1
+);
+
+% Isolated count for DrawOne, you can only play your draw one, if you've atleast played that artist this round
+constraint forall(p in Players, r in Rounds, a in Artists) (
+ visible_count_per_round_per_artist_per_player[r,p,a] >= DrawOneCard[r,p,a]
+);
\ No newline at end of file
diff git a/gameplay.mzn b/gameplay.mzn
index 93c047c..fdddfea 100644
 a/gameplay.mzn
+++ b/gameplay.mzn
@@ 8,9 +8,10 @@ include "artists.mzn";
include "dealing.mzn";
% Ignoring these for now
% include "double.mzn";
% include "drawone.mzn";
+include "drawone.mzn";
% include "hidden.mzn";
% include "simultaneous.mzn";
+include "symbols.mzn";
/*
This file includes the core gameplay rules
@@ 42,18 +43,26 @@ constraint forall(r in Rounds, p in Players) (
% Calculate maximum cards any player can play in first round
array[Rounds,Players] of var int: StartingCardsInHand;
% Round 1 Starting Hand is 13 cards
constraint forall(p in Players) (StartingCardsInHand[Round1,p] = CardsDealtPerRound[Round1]);
constraint forall(p in Players) (
 StartingCardsInHand[Round2,p] = StartingCardsInHand[Round1,p]  TotalCardsPlayed[Round1,p] + CardsDealtPerRound[Round2]
+
+% Starting Cards for First Round = Cards Dealt
+constraint forall(p in Players) (
+ StartingCardsInHand[Round1,p] = CardsDealtPerRound[Round1]
+);
+
+% Playable Card Calculation in drawone.mzn
+
+% Starting cards for subsequent rounds = Playable Cards from previous round  Cards Played + cards dealt
+constraint forall(p in Players) (
+ StartingCardsInHand[Round2,p] = PlayableCards[Round1,p]  TotalCardsPlayed[Round1,p] + CardsDealtPerRound[Round2]
);
constraint forall(p in Players) (
 StartingCardsInHand[Round3,p] = StartingCardsInHand[Round2,p]  TotalCardsPlayed[Round2,p] + CardsDealtPerRound[Round3]
+constraint forall(p in Players) (
+ StartingCardsInHand[Round3,p] = PlayableCards[Round2,p]  TotalCardsPlayed[Round2,p] + CardsDealtPerRound[Round3]
);
constraint forall(p in Players) (
 StartingCardsInHand[Round4,p] = StartingCardsInHand[Round3,p]  TotalCardsPlayed[Round3,p] + CardsDealtPerRound[Round4]
+constraint forall(p in Players) (
+ StartingCardsInHand[Round4,p] = PlayableCards[Round3,p]  TotalCardsPlayed[Round3,p] + CardsDealtPerRound[Round4]
);
+% For every round, you must start with more cards than you play
constraint forall(r in Rounds, p in Players) (
StartingCardsInHand[r,p] >= TotalCardsPlayed[r,p]
);
@@ 82,39 +91,32 @@ constraint first_player[Round4] = if last_player[Round3] = card(Players) then 1
constraint Pn1 = if (first_player[Round1] < last_player[Round1]) then
first_player[Round1]..last_player[Round1]
 else
+ else
Players diff (first_player[Round1]..last_player[Round1])
endif;
constraint Pn2 = if (first_player[Round2] < last_player[Round2]) then
first_player[Round2]..last_player[Round2]
 else
+ else
Players diff first_player[Round2]..last_player[Round2]
endif;
constraint Pn3 = if (first_player[Round3] < last_player[Round3]) then
first_player[Round3]..last_player[Round3]
 else
+ else
Players diff first_player[Round3]..last_player[Round3]
endif;
constraint Pn4 = if (first_player[Round4] < last_player[Round4]) then
first_player[Round4]..last_player[Round4]
 else
+ else
Players diff first_player[Round4]..last_player[Round4]
endif;
constraint forall(p in Players) (TotalCardsPlayed[Round1, p] = if p in Pn1 then NominalTurnCount[Round1] else NominalTurnCount[Round1]  1 endif);
constraint forall(p in Players) (TotalCardsPlayed[Round2, p] = if p in Pn2 then NominalTurnCount[Round2] else NominalTurnCount[Round2]  1 endif);
constraint forall(p in Players) (TotalCardsPlayed[Round3, p] = if p in Pn3 then NominalTurnCount[Round3] else NominalTurnCount[Round3]  1 endif);
constraint forall(p in Players) (TotalCardsPlayed[Round4, p] = if p in Pn3 then NominalTurnCount[Round4] else NominalTurnCount[Round4]  1 endif);

% Total Playable Cards for any round are set equal to the Starting Hand
% TODO: Move this to `drawone`
array[Rounds,Players] of var int: PlayableCards;
constraint forall(p in Players, r in Rounds) (
 PlayableCards[r,p] = StartingCardsInHand[r,p]
);
+constraint forall(p in Players) (TotalCardsPlayed[Round4, p] = if p in Pn3 then NominalTurnCount[Round4] else NominalTurnCount[Round4]  1 endif);
\ No newline at end of file
diff git a/symbols.mzn b/symbols.mzn
new file mode 100644
index 0000000..1488fab
 /dev/null
+++ b/symbols.mzn
@@ 0,0 +1,15 @@
+% This file maintains common symbol constraints
+% Since we are modelling each symbol individually (and not "onacard"), this results in
+% symbol counts being calculated in isolation.
+% Sanity constraints on each symbol file individually ensure
+% that atleast one card of the symbol was played by the correct played
+% they do not ensure that across symbols
+% and hence, the same card may end up getting counted twice for different symbols.
+% this file prevents such issues
+% and may ultimately end up replacing separate symbol counters
+
+constraint forall(r in Rounds, a in Artists) (
+ sum (p in Players) (
+ visible_count_per_round_per_artist_per_player[r,p,a]  DrawOneCard[r,p,a]
+ ) >= awards_per_round_per_artist[r,a]
+);
\ No newline at end of file