Dominion Strategy Forum

Please login or register.

Login with username, password and session length
Pages: 1 [2]  All

Author Topic: Complexity of Dominion  (Read 1946 times)

0 Members and 1 Guest are viewing this topic.

chipperMDW

  • Apprentice
  • *****
  • Online Online
  • Posts: 259
  • Respect: +558
    • View Profile
Re: Complexity of Dominion
« Reply #25 on: October 05, 2018, 01:08:24 pm »
+1

Both of those are ongoing effects, which will compound in the obvious way: once you've Inherited an Action, all of your Estates cost $2 while a Quarry is in play. The relative ordering of buying Inheritance, taking ownership of the Estate and playing a Quarry is unimportant.

First, I think you may have typoed this statement. All your (Inherited) Estates cost $0, not $2, while Quarry is in play. That's the "obvious way" they compound, right?

Second, nowhere was I claiming that the order of buying Inheritance and playing Quarry had any effect on the resulting state. In fact, I'm claiming the exact opposite: the order in which the effects come into being cannot matter; the result must be the same either way (your Inherited Estates cost $0). And in order for that to be the case, there must be an understood evaluation order.

Say Q(x) represents a game state like x, but with Quarry's effect applied. I(x) represents a game state like x, but with the effects of Inheriting Village applied. G is a game state where everything is as printed.

If you evaluate Q(I(G)), you get $0 Estates. If you evaluate I(Q(G)), you get $2 Estates. In order to get a consistent result of $0 Estates, you should only ever be picking Q(I(G)) whenever you have to evaluate both of them.

Now, say you're at G, and you play Quarry. You're now at Q(G). Now Inherit Village. You can't just apply I(x) to the current game state, or you end up at the incorrect I(Q(G)) and you have $2 Estates. Instead, you have to insert the evaluation of I at an intermediate point in order to get Q(I(G)). In other words, you need a rule that says I(x) effects to be evaluated inside the parentheses of Q(x) effects.

Donald says there'd need to be a "timing" if you had both cost increasers and cost reducers. Presumably, that's because cost reducers say they cap the result at $0. Say B(x) is the effect of Bridge and J(x) is a hypothetical effect that increases all card costs by $1. If you evaluated B(J(G)), then Coppers would cost $0 because the effects canceled out; if you evaluated J(B(G)), then Coppers would cost $1 because the cost increase was not "visible" at the point you evaluated B, so it capped at $0. Presumably, you'd always want Coppers to cost $0 in this case, so you'd need a rule that says increasers have to be evaluated "before" (inside the parentheses of) reducers.*

I'm saying that the exact same thing is true with Quarry and Inheritance: the order of evaluation matters. In either case, you can say that the result is "obvious," but you'd still following a rule (perhaps an obvious, intuitively understood rule) to get the result, even if you didn't realize it. And everyone else would need to follow the same rule in order to get the same result you did.


* Or else say that the capping at $0 isn't part of B but something that gets evaluated after all the cost-modifying effects, which I recall was also considered as an option.
Logged

crj

  • Jester
  • *****
  • Offline Offline
  • Posts: 994
  • Respect: +1078
    • View Profile
Re: Complexity of Dominion
« Reply #26 on: October 05, 2018, 02:16:50 pm »
0

First, I think you may have typoed this statement.
Ooops, yeah, fixed.

Quote
Say Q(x) represents a game state like x, but with Quarry's effect applied. I(x) represents a game state like x, but with the effects of Inheriting Village applied. G is a game state where everything is as printed.

If you evaluate Q(I(G)), you get $0 Estates. If you evaluate I(Q(G)), you get $2 Estates. In order to get a consistent result of $0 Estates, you should only ever be picking Q(I(G)) whenever you have to evaluate both of them.
Not so.

Firstly, I'll clarify that I(x) should actually be with the effects of player P Inheriting Village applied.

Q(x) modifies the algorithm for determining card cost, until the Quarry leaves play. It reduces the cost for any card that has Action type.

I(x) modifies (amongst other things) the algorithm for determining card type, until the end of the game. If a card is an Estate and is owned by P, it does have that type if Village has that type.

If you ask the question "what is the cost of card C?", you'll get the same result on Q(I(G)) or I(Q(G)).

That Q(x) and I(x), rather than instantaneously adjusting card costs or types, alter the algorithm by which costs or types will be determined in future, is what I mean by saying the effect is "ongoing".
Logged

crj

  • Jester
  • *****
  • Offline Offline
  • Posts: 994
  • Respect: +1078
    • View Profile
Re: Complexity of Dominion
« Reply #27 on: October 05, 2018, 02:30:35 pm »
+1

For a concrete example of why you have to think in those terms, consider this:
  • You play Quarry A
  • You play Quarry B
  • You buy Bonfire and trash Quarry A
  • You buy a Village
How much does the Village cost when you buy it? $1. You cannot reach that conclusion by reducing the cost of Village by $2 (minimum $0) when you play Quarry A, then by another $2 (minimum $0) when you play Quarry B, then increasing it by $2 when you remove Quarry A from play.

The only correct option is to think in terms of evaluating the cost of Village at the moment when you buy it, using an algorithm which is dynamically adjusted by the course of play.
Logged

GendoIkari

  • Adventurer
  • ******
  • Offline Offline
  • Posts: 7377
  • Respect: +8198
    • View Profile
Re: Complexity of Dominion
« Reply #28 on: October 05, 2018, 04:45:48 pm »
+1

First, I think you may have typoed this statement.
Ooops, yeah, fixed.

Quote
Say Q(x) represents a game state like x, but with Quarry's effect applied. I(x) represents a game state like x, but with the effects of Inheriting Village applied. G is a game state where everything is as printed.

If you evaluate Q(I(G)), you get $0 Estates. If you evaluate I(Q(G)), you get $2 Estates. In order to get a consistent result of $0 Estates, you should only ever be picking Q(I(G)) whenever you have to evaluate both of them.
Not so.

Firstly, I'll clarify that I(x) should actually be with the effects of player P Inheriting Village applied.

Q(x) modifies the algorithm for determining card cost, until the Quarry leaves play. It reduces the cost for any card that has Action type.

I(x) modifies (amongst other things) the algorithm for determining card type, until the end of the game. If a card is an Estate and is owned by P, it does have that type if Village has that type.

If you ask the question "what is the cost of card C?", you'll get the same result on Q(I(G)) or I(Q(G)).

That Q(x) and I(x), rather than instantaneously adjusting card costs or types, alter the algorithm by which costs or types will be determined in future, is what I mean by saying the effect is "ongoing".

Not sure I agree with the bolded part. In order to evaluate I(Q(G)), you have to evaluate Q(G) first. And when you do, you would end up with "Estates cost ", because they aren't actions. Then you would evaluate I(result); and the Estates would become Actions.

We know this isn't correct by the rules.

Actually, I think what happens here is an implied version of what Magic calls a dependency. Because the effect of Inheritance would change the resulting effect of Quarry, it's evaluated first. The rules just don't feel the need to have such specifics in it; because it's intuitive. (People ask "Is Estate an Action"? And immediately see "Yes, it is").

Because Dominion doesn't spell out these rules, it avoids any sort of "Dependency loop"; where 2 different effects could be changed by the other. For example, imagine if Inheritance actually said "Your Estates that cost gain the abilities and types of that card". While Inheritance by itself could have worked that way, it would create a dependency loop with Quarry, and thus such a thing would simply be avoided in the card creation process.*

* It's a loop because in order to see if the Estate is an action, we need to check how much it costs, which involves Quarry. But since Quarry only works on actions, we need to check the type, so we look at Inheritance. But we don't know if Inheritance is working or not, because that depends on the cost. Etc forever.
« Last Edit: October 05, 2018, 04:47:32 pm by GendoIkari »
Logged
Check out my F.DS extension for Chrome! Card links; Dominion icons, and maybe more! http://forum.dominionstrategy.com/index.php?topic=13363.0

Thread for Firefox version:
http://forum.dominionstrategy.com/index.php?topic=16305.0

crj

  • Jester
  • *****
  • Offline Offline
  • Posts: 994
  • Respect: +1078
    • View Profile
Re: Complexity of Dominion
« Reply #29 on: October 05, 2018, 05:14:54 pm »
0

In order to evaluate I(Q(G)), you have to evaluate Q(G) first. And when you do, you would end up with "Estates cost ", because they aren't actions.
No, you don't end up with "Estates cost $2". You end up with a game state in which if you were to ask "what is the cost of this Estate in my hand?" you would be told "$2".

Quote
Then you would evaluate I(result); and the Estates would become Actions.
No, Estates don't become Actions. You end up with a game state in which if you were to ask "is this Estate in my hand an Action?" you would be told "yes".

And, as a result, if you then ask "what is the cost of this Estate in my hand?" with a Quarry in play, you will be told "$0". Because you're asking the entire question of state I(Q(G)), rather than asking Q(G) part of the question, memoizing the result and then asking the remainder of the question if I(Q(G)).
Logged

GendoIkari

  • Adventurer
  • ******
  • Offline Offline
  • Posts: 7377
  • Respect: +8198
    • View Profile
Re: Complexity of Dominion
« Reply #30 on: October 05, 2018, 05:19:20 pm »
0

In order to evaluate I(Q(G)), you have to evaluate Q(G) first. And when you do, you would end up with "Estates cost ", because they aren't actions.
No, you don't end up with "Estates cost $2". You end up with a game state in which if you were to ask "what is the cost of this Estate in my hand?" you would be told "$2".

Quote
Then you would evaluate I(result); and the Estates would become Actions.
No, Estates don't become Actions. You end up with a game state in which if you were to ask "is this Estate in my hand an Action?" you would be told "yes".

And, as a result, if you then ask "what is the cost of this Estate in my hand?" with a Quarry in play, you will be told "$0". Because you're asking the entire question of state I(Q(G)), rather than asking Q(G) part of the question, memoizing the result and then asking the remainder of the question if I(Q(G)).

I agree with what you're getting at. I just wouldn't have put that as I(Q(G)) or Q(G(I)) because normally with nested functions, you don't ever "go back" to the inner function once it is evaluated. But really I think we're all saying the same basic thing here.
Logged
Check out my F.DS extension for Chrome! Card links; Dominion icons, and maybe more! http://forum.dominionstrategy.com/index.php?topic=13363.0

Thread for Firefox version:
http://forum.dominionstrategy.com/index.php?topic=16305.0

Commodore Chuckles

  • Minion
  • *****
  • Offline Offline
  • Posts: 592
  • Shuffle iT Username: Commodore Chuckles
  • Respect: +900
    • View Profile
Re: Complexity of Dominion
« Reply #31 on: October 05, 2018, 06:42:51 pm »
0

*Edit*: The closest we have is the wording on Outpost, which says you draw 3 cards for your next turn. This needs to be “applied” before Expedition; otherwise you can logically conclude that if you play Outpost and buy Expedition; you should only draw 3. I think the way it works is that the Outpost rule is actually that it makes you draw 2 fewer cards; rather than makes you draw 3.

I don't think the "2 fewer cards" idea works though, because of what happens with Donate. If you play Outpost and then buy Donate, your Outpost turn will have 5 cards because Donate tells you to draw 5. Outpost's wording doesn't reduce this to 3, it just gets ignored. Unless the online implementation got this wrong.
Logged

GendoIkari

  • Adventurer
  • ******
  • Offline Offline
  • Posts: 7377
  • Respect: +8198
    • View Profile
Re: Complexity of Dominion
« Reply #32 on: October 05, 2018, 06:49:37 pm »
0

*Edit*: The closest we have is the wording on Outpost, which says you draw 3 cards for your next turn. This needs to be “applied” before Expedition; otherwise you can logically conclude that if you play Outpost and buy Expedition; you should only draw 3. I think the way it works is that the Outpost rule is actually that it makes you draw 2 fewer cards; rather than makes you draw 3.

I don't think the "2 fewer cards" idea works though, because of what happens with Donate. If you play Outpost and then buy Donate, your Outpost turn will have 5 cards because Donate tells you to draw 5. Outpost's wording doesn't reduce this to 3, it just gets ignored. Unless the online implementation got this wrong.

No, I think it works... by the time Donate does anything, Outpost will have already made you draw 3 cards instead of 5. You don't do anything for Donate until "after this turn" which includes after you've drawn your hand for next turn. So if you buy Donate on a turn that you played Outpost, you will draw 3 cards instead of 5, then Donate will make you draw everything else, then eventually draw 5.

*Edit* This can actually matter... if you had the -1 card token on your deck, then it's important that you draw your next-turn hand first, before dealing with Donate. That way you still get the full 5 cards from Donate. If you skipped the regular next-turn draw, then you'd have to draw 4 cards instead of 5 when drawing up after Donate.
« Last Edit: October 05, 2018, 06:50:58 pm by GendoIkari »
Logged
Check out my F.DS extension for Chrome! Card links; Dominion icons, and maybe more! http://forum.dominionstrategy.com/index.php?topic=13363.0

Thread for Firefox version:
http://forum.dominionstrategy.com/index.php?topic=16305.0

crj

  • Jester
  • *****
  • Offline Offline
  • Posts: 994
  • Respect: +1078
    • View Profile
Re: Complexity of Dominion
« Reply #33 on: October 05, 2018, 06:57:07 pm »
0

I agree with what you're getting at. I just wouldn't have put that as I(Q(G)) or Q(G(I)) because normally with nested functions
I wouldn't have put it that way either; it's not a natural way to express Dominion.

However, given other people have been using it, I've run with it, in order to explain in those terms how Dominion behaves.

Quote
you don't ever "go back" to the inner function once it is evaluated
That's taking a fairly narrow view of what G might be and how I or Q might manipulate it. For example, expressing the slightly simpler Wharf in the Python idiom:

Code: [Select]
class Wharf:
  @staticmethod
  def effect(g):
    g.cards(+2)
    g.buys(+1)
  @staticmethod
  def onPlay(g):
    effect(g)
    g.player().startOfTurn.append(effect)
Now when you do g = someWharfOrOther.onPlay(g), the start-of-turn part of Wharf becomes encapsulated within g and will, indeed, be called later. In that sense you do "go back" to the inner function.
Logged

GendoIkari

  • Adventurer
  • ******
  • Offline Offline
  • Posts: 7377
  • Respect: +8198
    • View Profile
Re: Complexity of Dominion
« Reply #34 on: October 06, 2018, 12:02:38 am »
0

I agree with what you're getting at. I just wouldn't have put that as I(Q(G)) or Q(G(I)) because normally with nested functions
I wouldn't have put it that way either; it's not a natural way to express Dominion.

However, given other people have been using it, I've run with it, in order to explain in those terms how Dominion behaves.

Quote
you don't ever "go back" to the inner function once it is evaluated
That's taking a fairly narrow view of what G might be and how I or Q might manipulate it. For example, expressing the slightly simpler Wharf in the Python idiom:

Code: [Select]
class Wharf:
  @staticmethod
  def effect(g):
    g.cards(+2)
    g.buys(+1)
  @staticmethod
  def onPlay(g):
    effect(g)
    g.player().startOfTurn.append(effect)
Now when you do g = someWharfOrOther.onPlay(g), the start-of-turn part of Wharf becomes encapsulated within g and will, indeed, be called later. In that sense you do "go back" to the inner function.

I get you. It does lead to a potential infinite loop though; as you would end up with in the case of the Inheritance that only works on $2 Estates.
Logged
Check out my F.DS extension for Chrome! Card links; Dominion icons, and maybe more! http://forum.dominionstrategy.com/index.php?topic=13363.0

Thread for Firefox version:
http://forum.dominionstrategy.com/index.php?topic=16305.0

chipperMDW

  • Apprentice
  • *****
  • Online Online
  • Posts: 259
  • Respect: +558
    • View Profile
Re: Complexity of Dominion
« Reply #35 on: October 06, 2018, 01:29:08 am »
0

For a concrete example of why you have to think in those terms, consider this:
  • You play Quarry A
  • You play Quarry B
  • You buy Bonfire and trash Quarry A
  • You buy a Village
How much does the Village cost when you buy it? $1. You cannot reach that conclusion by reducing the cost of Village by $2 (minimum $0) when you play Quarry A, then by another $2 (minimum $0) when you play Quarry B, then increasing it by $2 when you remove Quarry A from play.

The only correct option is to think in terms of evaluating the cost of Village at the moment when you buy it, using an algorithm which is dynamically adjusted by the course of play.

I described Q(x) as a state where the costs of actions are $2 less than they are in x. I would have hoped it was clear that moving one Quarry out of play would cause one Q to be removed from the evaluation order (i.e. the game state would change from Q(Q(G)) to Q(G)), so the resulting final state would evaluate an action's cost by applying a single cost reduction of $2 to the printed cost. It would say Village costs $1. I'm not sure whether something I described actually led you to understand that I'd be trying to add $2 whenever I removed a Quarry from play, but that's not the case.

I think I understand the model you're describing. It's different from how I think about it (which is closer to the layer system described in M:tG's rules), but I think it provides the same results, and it's nice that your algorithm imposes an implicit dependency order on evaluations so that the "intuitive and obvious" result is the only one you can arrive at.
Logged

Honkeyfresh

  • Bishop
  • ****
  • Offline Offline
  • Posts: 114
  • Respect: +52
    • View Profile
Re: Complexity of Dominion
« Reply #36 on: October 06, 2018, 11:30:53 pm »
+2

To be fair, even the Laws of Duplicate Bridge are 166 pages long.

It's because, if you have cost reduction, you can gain a free copy of a card whose baseline cost is more than $6.

Logged

Jeebus

  • Saboteur
  • *****
  • Offline Offline
  • Posts: 1236
  • Shuffle iT Username: jeebus
  • Respect: +912
    • View Profile
Re: Complexity of Dominion
« Reply #37 on: October 07, 2018, 07:35:20 pm »
0

The closest we have is the wording on Outpost, which says you draw 3 cards for your next turn. This needs to be “applied” before Expedition; otherwise you can logically conclude that if you play Outpost and buy Expedition; you should only draw 3. I think the way it works is that the Outpost rule is actually that it makes you draw 2 fewer cards; rather than makes you draw 3.

I always interpreted it like this: Outpost changes your next hand to 3, so it has to trigger before you draw (effectively "when you would draw your next hand"). You could interpret Expedition and Flag the same way, but since they tell you to draw more cards, I find it more natural to think of them as triggering afterwards ("when you draw your next hand, draw x extra cards"). This way we don't need to apply any extra timing rule or change the math on Outpost.

Jeebus

  • Saboteur
  • *****
  • Offline Offline
  • Posts: 1236
  • Shuffle iT Username: jeebus
  • Respect: +912
    • View Profile
Re: Complexity of Dominion
« Reply #38 on: October 07, 2018, 07:45:28 pm »
+1

The base rules document for Dominion is about 8 pages of relatively user-friendly text with pictures (excluding parts that aren't rules). If you tack on the unique rules for all the expansions, it might take another 6 pages if you condense examples. The currently complete rules document would be about 10-15 pages long, depending on formatting and verbosity of the examples given. Most of the expansions rules documents are taken up by the notes (which aren't rules relevant in nearly all cases) and recommended sets (thank you Donald, these are wonderful).

It wouldn't be as detailed in timing as other documents meticulously made by others here, but would be essentially complete.

Well, my rules document devotes 30 pages to the actual rules for all explansions, including going through all contents and setup of all expensions, with a few pictures and examples, but not including most of the special card-specific clarifications and rulings. And the rules are written pretty densely, more so than in the official rules. Sure, there are some very specific and corner-case rules included (in small print), but since we're comparing it to a very detailed Magic document...

Maybe it's better to compare words. Those 30 pages of Dominion rules amount to about 15,000 words. The Magic document has about 116,000 words, so 13% of the complexity maybe?

chipperMDW

  • Apprentice
  • *****
  • Online Online
  • Posts: 259
  • Respect: +558
    • View Profile
Re: Complexity of Dominion
« Reply #39 on: October 07, 2018, 08:53:36 pm »
0

For a concrete example of why you have to think in those terms, consider this:
  • You play Quarry A
  • You play Quarry B
  • You buy Bonfire and trash Quarry A
  • You buy a Village
How much does the Village cost when you buy it? $1. You cannot reach that conclusion by reducing the cost of Village by $2 (minimum $0) when you play Quarry A, then by another $2 (minimum $0) when you play Quarry B, then increasing it by $2 when you remove Quarry A from play.

The only correct option is to think in terms of evaluating the cost of Village at the moment when you buy it, using an algorithm which is dynamically adjusted by the course of play.

So, I think I figured out why you made this comment, and why you don't agree that Q(I(G)) produces a different result from I(Q(G)). You must still be thinking that Q(I(G)) is supposed to represent a sequence of states that a game progresses through as time passes. Like, you think Q(x) effectively means "play a Quarry." I explained before that that's not what I mean, and that the notation is intended to describe an evaluation order. The expressions inside parentheses don't represent past states, but substates.

I'm not sure how to explain better than I already have in text, so I wrote this instead. It's a Python script that uses the model I've been describing to process the effects we've been talking about in a very simple Dominion-inspired game state. The evaluation of effects is ordered by a simple priority order, with Inheritance always happening before Quarry (although it could be modified to use a dependency system like you effectively describe). Hopefully the code speaks for itself and makes it more clear what I've been saying. (Assuming you want to read/run it at all.)

(It assumes all Estates are "yours," by the way.)

Code: [Select]
class Substate(object):
    def __init__(self, effects):
        self.effects = effects
    def has_outstanding_effects(self):
        return len(self.effects) > 0
    def next_state(self):
        effects = sorted(self.effects, key = lambda e: e.priority)
        return effects[0](self, effects[1:])

# First element is cost; second element is whether it's an action
carddefs = { "Estate": [2, False], "Village": [3, True] }

class RawState(Substate):
    def __init__(self):
        Substate.__init__(self, [])
    def card_cost(self, name):
        return carddefs[name][0]
    def card_is_action(self, name):
        return carddefs[name][1]
    def as_str(self):
        return "G"

class ModifiedState(Substate):
    def __init__(self, underlying_state, outstanding_effects):
        Substate.__init__(self, outstanding_effects)
        self.underlying_state = underlying_state
    def card_cost(self, name):
        return self.underlying_state.card_cost(name)
    def card_is_action(self, name):
        return self.underlying_state.card_is_action(name)
    def as_str(self):
        return self.sym() + "(" + self.underlying_state.as_str() + ")"

class InheritanceEffect(ModifiedState):
    def card_is_action(self, name):
        action = self.underlying_state.card_is_action(name)
        return True if name == "Estate" else action
    def sym(self):
        return "I"
    priority = 0

class QuarryEffect(ModifiedState):
    def card_cost(self, name):
        action = self.underlying_state.card_is_action(name)
        cost = self.underlying_state.card_cost(name)
        return max(0, cost - 2) if action else cost
    def sym(self):
        return "Q"
    priority = 1

class State:
    def __init__(self):
        self.raw = RawState()
    def upper_substate(self):
        substate = self.raw
        while substate.has_outstanding_effects():
            substate = substate.next_state()
        return substate
    def card_cost(self, name):
        return self.upper_substate().card_cost(name)
    def card_is_action(self, name):
        return self.upper_substate().card_is_action(name)
    def as_str(self):
        return "%s: Estate $%u, Village $%u" % (
            self.upper_substate().as_str(),
            self.card_cost("Estate"),
            self.card_cost("Village"))
    def play_quarry(self):
        self.raw.effects.append(QuarryEffect)
    def unplay_quarry(self):
        self.raw.effects.remove(QuarryEffect)
    def inherit_village(self):
        self.raw.effects.append(InheritanceEffect)

print "1. Play Quarry Before Buying Inheritance:"
s = State()
s.play_quarry()
s.inherit_village()
print "\t" + s.as_str() + "\n"

print "2. Buy Inheritance Before Playing Quarry"
s = State()
s.inherit_village()
s.play_quarry()
print "\t" + s.as_str() + "\n"

print "3. How Not To Do It"
InheritanceEffect.priority = 2
print "\t" + s.as_str() + "\n"
InheritanceEffect.priority = 0

print "4. Play Two Quarries..."
s = State()
s.play_quarry()
s.play_quarry()
print "\t" + s.as_str()
print "...Then Remove One From Play"
s.unplay_quarry()
print "\t" + s.as_str()

Here's the output:
Code: [Select]
1. Play Quarry Before Buying Inheritance:
        Q(I(G)): Estate $0, Village $1

2. Buy Inheritance Before Playing Quarry
        Q(I(G)): Estate $0, Village $1

3. How Not To Do It
        I(Q(G)): Estate $2, Village $1

4. Play Two Quarries...
        Q(Q(G)): Estate $2, Village $0
...Then Remove One From Play
        Q(G): Estate $2, Village $1

The first two cases demonstrate both orders of playing Quarry and buying Inheritance. They both produce the correct results (which are identical to one another), and they both result in state Q(I(G)) due to the priority used to decide the evaluation order of the two types of effects. The third case temporarily mucks around with the priority ordering to show that evaluating the effects in the order I(Q(G)) produces a different (incorrect) result. Also, the last case demonstrates that the model doesn't have the issue you were describing with playing multiple Quarries and then removing one from play.
Logged

theorel

  • Salvager
  • ****
  • Offline Offline
  • Posts: 66
  • Shuffle iT Username: theorel
  • Respect: +34
    • View Profile
Re: Complexity of Dominion
« Reply #40 on: October 08, 2018, 03:04:43 pm »
+1

So, your whole algorithm depends on effects taking place at an instance in time.  Which maybe comes from the Magic layers system, I dunno.

But Quarry's effect isn't written to take place at an instant in time...it's a continuous effect. As is inheritance's effect.  Which is what crj was trying to get at, I believe.  And indeed if you write the 2 effects in a sentence, the order is irrelevant.  i.e. Our gamestate is one where:
-"Estate is an action, and all actions' costs are reduced by 2"
-"All actions' costs are reduced by 2, and Estate is an action"
Now when you dig deep and say how did you decide that the estate's cost was reduced by 2?  We say, because of inheritance.  i.e. Inheritance applies before the cost-reduction...but that's not equivalent to Inheritance applying before Quarry.

When translating such continuous effects into code, or making them instantaneous otherwise, you must add additional constraints, because "we can't go back".  I believe in this case rather than saying it's a "priority" thing (which sounds arbitrary), it should be considered as a "dependency" thing.  Quarry's cost-reduction depends on type, Inheritance effects type, so Quarry depends on Inheritance.  Thus I(Q(G)) evaluated as a single point-effect doesn't make sense, because Q depends on I, and dependencies should always be evaluated first.
Logged

GendoIkari

  • Adventurer
  • ******
  • Offline Offline
  • Posts: 7377
  • Respect: +8198
    • View Profile
Re: Complexity of Dominion
« Reply #41 on: October 08, 2018, 03:49:11 pm »
+2

I believe in this case rather than saying it's a "priority" thing (which sounds arbitrary), it should be considered as a "dependency" thing.  Quarry's cost-reduction depends on type, Inheritance effects type, so Quarry depends on Inheritance.  Thus I(Q(G)) evaluated as a single point-effect doesn't make sense, because Q depends on I, and dependencies should always be evaluated first.

Right, this is exactly what I was trying to get at. And the rules of Magic even deal specifically with dependencies, saying that the evaluation of A would affect the evaluation of B, then you evaluate A first.

With Inheritance and Quarry, the list of cards that Quarry is affecting is modified by Inheritance. Thus, we either need to apply Inheritance first, OR we need to check Inheritance as a part of checking Quarry. Which why we think about that doesn't matter.

But if you had weird-Inheritance that only worked on Estates costing , then suddenly part of checking Inheritance means checking the cost of cards, which means checking Quarry. So either we need to apply Quarry first, or we need to check Quarry as part of checking Inheritance. Either way, we end up with a broken situation; a circular dependency. Dominion avoids this issue by never having circular dependencies.
Logged
Check out my F.DS extension for Chrome! Card links; Dominion icons, and maybe more! http://forum.dominionstrategy.com/index.php?topic=13363.0

Thread for Firefox version:
http://forum.dominionstrategy.com/index.php?topic=16305.0

chipperMDW

  • Apprentice
  • *****
  • Online Online
  • Posts: 259
  • Respect: +558
    • View Profile
Re: Complexity of Dominion
« Reply #42 on: October 08, 2018, 04:48:52 pm »
0

So, your whole algorithm depends on effects taking place at an instance in time.  Which maybe comes from the Magic layers system, I dunno.

But Quarry's effect isn't written to take place at an instant in time...it's a continuous effect. As is inheritance's effect.  Which is what crj was trying to get at, I believe.

It does not depend on "effects taking place at an instant in time." I'm aware that continuous effects are meant to always apply, and that they don't "occur" at instants in time.  There's absolutely no "time" involved in the algorithm I'm discussing (nor is time involved in Magic's layer system, for that matter). In these lines from the last section of my script:
Code: [Select]
print "1. Play Quarry Before Buying Inheritance:"
s = State()
s.play_quarry()
s.inherit_village()
print "\t" + s.as_str() + "\n"

the concept of "time passing" is present only in the instructions "play_quarry" and "inherit_village".  The game has only one state at any given time.  Those two are the only instructions that mutate the game's state from one moment in time to the next.  Specifically, it starts at an initial state, progresses to a state where a Quarry has been played, then progresses to a state where Inheritance has been bought.  The algorithm I'm discussing doesn't involve any of that (and, as is demonstrated, is independent of the time order in which those two things occurred).

The algorithm I'm discussing occurs entirely within the call to as_str(), which does not mutate the game state; it merely determines a string representation of the game's current state at this one instant in time.  Of course, the algorithm progresses through a series of steps in order to reach its result, where certain things happen logically before other things.  As any algorithm must do.  That doesn't mean that those steps represent a passage of time, or that the game was ever actually in the intermediate states you considered on the way to the final one.

Take the equation x = 2 + 3 * 5.  In order to determine x, you have to follow a sequence of steps.  You have to follow them in the correct order, and one step along the way is that you find one of the subexpressions is equal to 15.  That doesn't somehow mean that x is equal to 15 from that moment in time until you finish the next step, after which it "becomes" 17.  Just because you're following the steps in a procedure doesn't mean you're representing the passage of time.

Likewise, just because you consider a game state where Inheritance's effect has been evaluated, but not Quarry's, that doesn't mean you're saying the game actually has that state at any given point in time.  It's just an intermediate concept you use on the way to evaluating the actual state at a given moment.  Just like the subexpression 3 * 5 is an intermediate concept you use on the way to evaluating x.


Quote
When translating such continuous effects into code, or making them instantaneous otherwise, you must add additional constraints, because "we can't go back".  I believe in this case rather than saying it's a "priority" thing (which sounds arbitrary), it should be considered as a "dependency" thing.  Quarry's cost-reduction depends on type, Inheritance effects type, so Quarry depends on Inheritance.  Thus I(Q(G)) evaluated as a single point-effect doesn't make sense, because Q depends on I, and dependencies should always be evaluated first.

I entirely agree that a priority order is completely arbitrary, and would probably not be the desirable way to describe an evaluation order in Dominion.  I also agree that a dependency order is probably what is intended.

I used the priority order in the code only because my intent was to show that, as I keep saying, the order of evaluation matters.  Even if what you're evaluating is a game state at one instant in time.  The simplest way to represent an order of evaluation is to assign a number to each thing you're evaluating. I wasn't trying to write a complete Dominion rules engine there.

My point, from the start, was that a hypothetical Dominion "Comprehensive Rules" document would explain what order to evaluate those effects. Nowhere am I saying that "dependency order" is somehow unacceptable. I'm just saying it's not called out anywhere (and doesn't really need to be, as far as I'm concerned). So one reason that the page counts of the rules that have been written for Dominion and those written for Magic are not directly comparable is because Magic's Comprehensive Rules are... more comprehensive. There are topics covered in Magic's rules that are not covered in Dominion's, but are nonetheless present (in fewer instances) there.
Logged

chipperMDW

  • Apprentice
  • *****
  • Online Online
  • Posts: 259
  • Respect: +558
    • View Profile
Re: Complexity of Dominion
« Reply #43 on: October 09, 2018, 12:05:38 am »
0

That doesn't mean that... the game was ever actually in the intermediate states you considered on the way to the final one.

Actually, let me take this one statement back.  The game's state actually is identical to all the intermediate states when you consider the intermediate states to include the effects that have yet to be applied, which my script does.  Each iteration of the loop in upper_substate() essentially performs a simplification on the game state, transforming it into an exactly equivalent state where one effect that is outstanding in the "lower" state is no longer outstanding in the simpler state "above" it. Sorta like how a + or * "disappears" from an expression when you transform it to an equivalent expression by applying that operator.
Logged

ackmondual

  • Tactician
  • *****
  • Offline Offline
  • Posts: 418
  • Respect: +260
    • View Profile
Re: Complexity of Dominion
« Reply #44 on: October 17, 2018, 10:30:21 pm »
0

In Dominion, the complexity is there on the table in front of you. There are no complexity surprises lurking later in someone else's deck, and the kingdom can be fine-tuned to the tastes and skill levels of the other players if you like.

That's the key. This is what I can't handle with Magic. I don't even theoretically know what's possible at the start of the game. It's also one of the reasons I love Dominion so much. You can look at the board, read the cards, and you're theoretically on the same level of understanding as anyone else. Yes, you won't notice some of the interactions straight away, but you theoretically could. There's nothing your opponent knows that you couldn't find out by thinking it through.
I go further.... I bring up all the fine points mentioned in the rulebook for each card's "FAQ".  For example, point out that with Embargo, if you gain a card (like with Workshop, which also happens to be in the supply  ;)), this gains you the card, but it's NOT buying it, so you can get around having to gain a Curse from any Embargo tokens on it.  I'd rather not have an advantage from that knowledge if we'd like a more even game

If they want to discover it all on their own, then I'll respect that request too.
Logged
Village, +2 Actions.  Village, +3 Actions.  Village, +4 Actions.  Village, +5 Actions.  Village, +6 Actions.  Village, +7 Actions.  Workers Village, +2 Buys, +8 Actions.  End Action Phase.  No Treasures to play.  No buy.  No Night cards to play
Pages: 1 [2]  All
 

Page created in 0.135 seconds with 20 queries.