Dominion Strategy Forum

Please login or register.

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

Author Topic: Scripting Dominion  (Read 19133 times)

0 Members and 1 Guest are viewing this topic.

silby

  • Swindler
  • ***
  • Offline Offline
  • Posts: 16
  • Respect: +23
    • View Profile
Scripting Dominion
« on: August 08, 2016, 12:03:09 pm »
+4

I have some questions about the implementation of the Dominion game engine that might be unanswerable or boring and maybe Stef/SCSN are unlikely to respond to this far in advance, but I'm extremely curious, so here goes:

I would imagine that any reasonable implementation of a game like Dominion would be based around an "event loop" that handles the phases of the turn (start, action, buy, cleanup, interturn) and cards that are scripted to trigger various side-effects when they're eligible to be played. All well and good. Many Dominion cards are built out of primitive effects that are not very complicated to script, and interact in predictable ways. (+1 Card/action/buy/coin, look at a card, reveal a card, etc.) Other cards have (seemingly?) unique effects, most notably Possession, which in the rules for humans is heavily annotated and some cards have FAQs that clarify precisely how they interact with Possession.

So my initial questions about this for the developers are these, if they care to reveal their approach at all:

- Am I even imagining a design similar to what you're building for the game engine?
- How many "primitive" game-state operations are there? What about higher-order operations like predicates or alternates?
- How many cards have an operation only used for that card?
- Are there any card interactions that have a true corner-case implementation, where the engine produces the wrong effect when A and B interact, but there's no feasible way to correct the individual components to get the right effect?
- I sure would be delighted for card-creation tools to be made available to players with the full power of the engine. I know there's lots of reasons to not ever do this, which I accept. This isn't a question.
Logged

-Stef-

  • Board Moderator
  • *
  • Offline Offline
  • Posts: 1574
  • Respect: +4419
    • View Profile
Re: Scripting Dominion
« Reply #1 on: August 09, 2016, 06:13:54 am »
+23

I am going to answer you a little bit. One of the cool things about two forum members making the new implementation should be a closer distance between the fans and the development. So far I've been very silent, but that is mostly due to time constraints. Also I don't really know where answering you leads to. One of the bad things that might happen is that it leads to a lot of other questions that people also want answered, and at that point I'm just going to say no, not now, now I'm actually going to continue making the game.

People often assume the difficult part about Dominion is in the cards, but so far it hasn't been. By far the most time consuming was setting up the Context. It isn't a lot of code, but I sort of had to figure out how Dominion actually works. The rules aren't always clear about this, and where they are they usually describe what should happen, not the things causing these consequences.

Anyway, as it turns out the Dominion Context has a Stack. Each element of the stack has a Map that assigns each Player a list of Abilities, and could have a cause (also an Ability). An Ability is basically just 'a piece of code' and apart from being executed it can also be cancelled, when one of its tracked cards get moved, covered up or shuffled due to something else.

The Stack is because if something is happening and it triggers something else, we first resolve something else before returning to the first thing.
The elements in the stack don't have just one list but a list for every player because of the turn-order resolution rule.
The cause is there because if something happens (say when-trash-a-card | when-your-turn-starts) and further down its stack some new trigger is set up (you draw a market square | your summoned hireling hits play) it has to still respond to the trigger-being-resolved.

- Are there any card interactions that have a true corner-case implementation, where the engine produces the wrong effect when A and B interact, but there's no feasible way to correct the individual components to get the right effect?
No. If this is the case, that simply means the engine is wrong. It has been, and maybe it still is, but if I find that out I will try to correct the engine (again), never start coding specific interactions.


I'll just give you the current implementation of two cards. One very simple, one a bit more complicated.
Code: [Select]
public class Margrave extends Card {

    public Margrave(int index, CardZone cardZone) {
        super(index, CardObjectName.MARGRAVE, cardZone);
    }

    public void onPlay(Context context, Player player, PlayCardAbility playCardAbility) throws SuspendExecution {
        player.drawCards(context, getCardObjectAssociation(), 3);
        player.addBuys(1);
        for (Player victim : playCardAbility.getVictims()) {
            victim.drawCards(context, getCardObjectAssociation(), 1);
            victim.discardDownTo(context, getCardObjectAssociation(), 3);
        }
    }

}

Code: [Select]
public class Beggar extends ReactionCard {

    private final List<CardMode> CARD_MODES = Arrays.asList (CardMode.BEGGAR_TOPDECK_FIRST, CardMode.BEGGAR_DISCARD_PILE_FIRST);


    public Beggar(int index, CardZone cardZone) {
        super(index, CardObjectName.BEGGAR, cardZone);
    }

    @Override
    public void onPlay(Context context, Player player, PlayCardAbility playCardAbility) throws SuspendExecution {
        for (int i = 0; i < 3; i++) {
            player.gain(context, getMe(), CardObjectName.COPPER, player.getHand());
        }
    }


    @Override
    protected boolean reactsTo(Event event, Context context) {
        boolean inHand = getZone().getZoneName() == ZoneName.HAND;
        boolean isWhen = event.getEventType() == EventType.WHEN;
        boolean isAttack = event.getAbility() instanceof PlayAttackAbility;
        if (!inHand || !isWhen || !isAttack) return false;
        PlayAttackAbility playAttackAbility = (PlayAttackAbility) event.getAbility();
        return playAttackAbility.getPlayer() != getPlayer();
    }

    @Override
    protected void createReaction(Event event, Context context, AbilityCollector abilityCollector) {
        abilityCollector.add(new BeggarAbility(getPlayer()));
    }


    private class BeggarAbility extends ReactionAbility {

        public BeggarAbility(Player player) {
            super(getMe(), player);
        }

        @Override
        public void resolve(Context context) throws SuspendExecution {
            player.discard(context, getCardObjectAssociation(), player.getHand(), getMe());
            NamedPile silverPile = context.getCommonPiles().getActualPile(CardObjectName.SILVER);
            CardMode cardMode = CardMode.BEGGAR_TOPDECK_FIRST;
            if (silverPile.size() == 1) {
                cardMode = context.ask(new WhatCardMode(CARD_MODES));
            }
            if (cardMode == CardMode.BEGGAR_TOPDECK_FIRST) {
                player.gain(context, getMe(), CardObjectName.SILVER, player.getDrawPile());
                player.gain(context, getMe(), CardObjectName.SILVER);
            } else {
                player.gain(context, getMe(), CardObjectName.SILVER);
                player.gain(context, getMe(), CardObjectName.SILVER, player.getDrawPile());
            }
        }
    }

}
Logged
Join the Dominion League!

werothegreat

  • Adventurer
  • ******
  • Offline Offline
  • Posts: 8172
  • Shuffle iT Username: werothegreat
  • Let me tell you a secret...
  • Respect: +9630
    • View Profile
Re: Scripting Dominion
« Reply #2 on: August 09, 2016, 08:44:52 am »
0

I notice Margrave does not have an "Attack" tag anywhere (in contrast to Beggar, which extends ReactionCard) - I'm assuming the fact that it has a "victim" means you resolve Attack triggers when victims are involved?  Are Masquerade and Possession dealt with differently, then?

EDIT: Also, any particular reason why Beggar's onPlay method has an @Override tag, while Margrave's does not?
« Last Edit: August 09, 2016, 08:49:37 am by werothegreat »
Logged
Contrary to popular belief, I do not run the wiki all on my own.  There are plenty of other people who are actively editing.  Go bother them!

Check out this fantasy epic adventure novel I wrote, the Broken Globe!  http://www.amazon.com/Broken-Globe-Tyr-Chronicles-Book-ebook/dp/B00LR1SZAS/

-Stef-

  • Board Moderator
  • *
  • Offline Offline
  • Posts: 1574
  • Respect: +4419
    • View Profile
Re: Scripting Dominion
« Reply #3 on: August 09, 2016, 08:54:47 am »
+3

I notice Margrave does not have an "Attack" tag anywhere (in contrast to Beggar, which extends ReactionCard) - I'm assuming the fact that it has a "victim" means you resolve Attack triggers when victims are involved?  Are Masquerade and Possession dealt with differently, then?
We have the card types assigned to the card names. Margrave knows it's called CardObjectName.MARGRAVE, and it can deduce it's an attack that way.
If I would replace the name with CardObjectName.SMITHY, it would still function as a Margrave, except that nobody could defend against it (and the client would show you an image of smithy)

By the time we get to the 'victim' part it would be too late. You decide whether to reveal moat before the attacking player draws cards.
The whole possibly-removing-victims has already happened by the time Margraves onPlay is executed.
Logged
Join the Dominion League!

Witherweaver

  • Adventurer
  • ******
  • Offline Offline
  • Posts: 6476
  • Shuffle iT Username: Witherweaver
  • Respect: +7866
    • View Profile
Re: Scripting Dominion
« Reply #4 on: August 09, 2016, 10:09:49 am »
0

I notice Margrave does not have an "Attack" tag anywhere (in contrast to Beggar, which extends ReactionCard) - I'm assuming the fact that it has a "victim" means you resolve Attack triggers when victims are involved?  Are Masquerade and Possession dealt with differently, then?
We have the card types assigned to the card names. Margrave knows it's called CardObjectName.MARGRAVE, and it can deduce it's an attack that way.
If I would replace the name with CardObjectName.SMITHY, it would still function as a Margrave, except that nobody could defend against it (and the client would show you an image of smithy)

By the time we get to the 'victim' part it would be too late. You decide whether to reveal moat before the attacking player draws cards.
The whole possibly-removing-victims has already happened by the time Margraves onPlay is executed.

Interesting; is the idea that Reaction is the only card type that really merits having its own (derived) class, since it has things like reaction trigger and reaction ability?  Or are there other derived classes for other cards; could there be a potential problem with trying to extend more than one class? (It looks like Java?  I guess you can get around it with interfaces; I don't really know Java.)
Logged

PitzerMike

  • Young Witch
  • ****
  • Offline Offline
  • Posts: 131
  • Longtime Pearldiver
  • Respect: +110
    • View Profile
Re: Scripting Dominion
« Reply #5 on: August 09, 2016, 10:53:48 am »
0

Cool, i like it!
Logged

silby

  • Swindler
  • ***
  • Offline Offline
  • Posts: 16
  • Respect: +23
    • View Profile
Re: Scripting Dominion
« Reply #6 on: August 09, 2016, 04:05:13 pm »
+1

Thank you for the insightful peek behind the curtain! Per your concerns, I'll avoid peppering with follow-up questions, but any other "dev log" notes you might produce in the next few months/next year would be very interesting to me. (Maybe someday you'll tell us what all the parts of the context data structure are.)
Logged

2.71828.....

  • Saboteur
  • *****
  • Offline Offline
  • Posts: 1290
  • Shuffle iT Username: irrationalE
  • Respect: +1322
    • View Profile
Re: Scripting Dominion
« Reply #7 on: August 09, 2016, 08:48:34 pm »
+8

Thank you for the insightful peek behind the curtain! Per your concerns, I'll avoid peppering with follow-up questions, but any other "dev log" notes you might produce in the next few months/next year would be very interesting to me. (Maybe someday you'll tell us what all the parts of the context data structure are.)

"The Secret History of Dominion Online"
Logged
Man. I had four strips of bacon yesterday. Was one automatically undercooked, one automatically overcooked? No, let's put a stop to that right here, all four strips were excellent.

-Stef-

  • Board Moderator
  • *
  • Offline Offline
  • Posts: 1574
  • Respect: +4419
    • View Profile
Re: Scripting Dominion
« Reply #8 on: August 12, 2016, 03:47:13 pm »
+11

Dear f.ds,


One of the cards I posted here is in conflict with the Dominion rules, even though it would have been fine pre Adventures.
Why o why haven't you complained about that yet?
Logged
Join the Dominion League!

michaeljb

  • Saboteur
  • *****
  • Offline Offline
  • Posts: 1422
  • Shuffle iT Username: michaeljb
  • Respect: +2114
    • View Profile
Re: Scripting Dominion
« Reply #9 on: August 13, 2016, 04:08:23 am »
+2

Well, this isn't something broken by Adventures, but

Code: [Select]
if (silverPile.size() == 1) {
    cardMode = context.ask(new WhatCardMode(CARD_MODES));
}

seems to contradict this bit from Beggar's official FAQ:

"If there is only one Silver left, put it on your deck; if there are no Silvers left, you do not gain any."

The code looks to me like it only asks the user to pick an order when there is one Silver left, but the last Silver should just go on the deck.

I think you should always get to choose whether to topdeck the first or second Silver, and it looks like this code doesn't let you choose. But I'm struggling to find the case in Adventures where that matters.
You could have enough Duplicates on your Tavern mat to empty the Silver pile, and really don't want the trashing attack you're reacting to with the Beggar to kill one of your Silvers, so you gain to your discard pile first, Duplicate that Silver a bunch, and none are left to go on top?
Logged
🚂 Give 18xx games a chance 🚂

-Stef-

  • Board Moderator
  • *
  • Offline Offline
  • Posts: 1574
  • Respect: +4419
    • View Profile
Re: Scripting Dominion
« Reply #10 on: August 13, 2016, 08:55:43 am »
0

...

Yup that would exactly be it. I would happily add you to the list of alpha-testers, except you were already on it.
We are currently aiming for a closed alpha at the start of September.
Hopefully it goes well and we can expand the list a bit halfway through September.
Logged
Join the Dominion League!

gkrieg13

  • Minion
  • *****
  • Offline Offline
  • Posts: 509
  • Shuffle iT Username: gkrieg
  • Respect: +463
    • View Profile
Re: Scripting Dominion
« Reply #11 on: August 13, 2016, 09:10:04 am »
0

...

Yup that would exactly be it. I would happily add you to the list of alpha-testers, except you were already on it.
We are currently aiming for a closed alpha at the start of September.
Hopefully it goes well and we can expand the list a bit halfway through September.

Darn I knew I should've posted the solution!
Logged

Watno

  • Margrave
  • *****
  • Offline Offline
  • Posts: 2745
  • Shuffle iT Username: Watno
  • Respect: +2983
    • View Profile
Re: Scripting Dominion
« Reply #12 on: August 13, 2016, 10:34:32 am »
0

...

Yup that would exactly be it. I would happily add you to the list of alpha-testers, except you were already on it.
We are currently aiming for a closed alpha at the start of September.
Hopefully it goes well and we can expand the list a bit halfway through September.
so why would it have been fine before adventures?
Logged

Chris is me

  • Margrave
  • *****
  • Offline Offline
  • Posts: 2745
  • Shuffle iT Username: Chris is me
  • What do you want me to say?
  • Respect: +3458
    • View Profile
Re: Scripting Dominion
« Reply #13 on: August 13, 2016, 10:49:30 am »
+3

...

Yup that would exactly be it. I would happily add you to the list of alpha-testers, except you were already on it.
We are currently aiming for a closed alpha at the start of September.
Hopefully it goes well and we can expand the list a bit halfway through September.
so why would it have been fine before adventures?

Before Adventures, there was no mechanism to interrupt Beggar's Silver gaining with additional Silver gains. You get to choose where the first Silver goes when you can gain more than one, but not when you can gain only one - but after you gain the first Silver, Duplicate can be used to empty the rest of a low Silver pile before the second one is gained.

Two Silvers in pile -> React with Beggar -> choose to gain the non top decked Silver first -> Duplicate that Silver -> no Silvers on top of deck. The code doesn't seem to allow this option.
« Last Edit: August 13, 2016, 10:51:48 am by Chris is me »
Logged
Twitch channel: http://www.twitch.tv/chrisisme2791

bug me on discord

pm me if you wanna do stuff for the blog

they/them

Watno

  • Margrave
  • *****
  • Offline Offline
  • Posts: 2745
  • Shuffle iT Username: Watno
  • Respect: +2983
    • View Profile
Re: Scripting Dominion
« Reply #14 on: August 13, 2016, 11:13:29 am »
+1

The FAQ michael quoted says
Quote
If there is only one Silver left, put it on your deck.

The code
Code: [Select]
            CardMode cardMode = CardMode.BEGGAR_TOPDECK_FIRST;
            if (silverPile.size() == 1) {
                cardMode = context.ask(new WhatCardMode(CARD_MODES));
            }
            if (cardMode == CardMode.BEGGAR_TOPDECK_FIRST) {
                player.gain(context, getMe(), CardObjectName.SILVER, player.getDrawPile());
                player.gain(context, getMe(), CardObjectName.SILVER);
            } else {
                player.gain(context, getMe(), CardObjectName.SILVER);
                player.gain(context, getMe(), CardObjectName.SILVER, player.getDrawPile());
            }

asks the player wether he wants the first or the second silver on top of his deck if there is only one Silver left, while the only option should be to have the first silver (the only one gained) on top of the deck.

I think the correct code should be simply

Code: [Select]
                player.gain(context, getMe(), CardObjectName.SILVER, player.getDrawPile());
                player.gain(context, getMe(), CardObjectName.SILVER);


Logged

Watno

  • Margrave
  • *****
  • Offline Offline
  • Posts: 2745
  • Shuffle iT Username: Watno
  • Respect: +2983
    • View Profile
Re: Scripting Dominion
« Reply #15 on: August 13, 2016, 11:22:21 am »
+1

Or to be honest, I'm not sure what should be happening. I would believe that you first gain the 2 silvers, and then put one of them on top of your deck (if you gained any), but that doesn't work due to the fact that Donald stated somewhere that topdecked gains don't visit the discard pile.

I'm not sure wether the following should be allowed:
Code: [Select]
Reveal Trader
   Gain Silver
      Gain something else somehow
           Topdeck that something with Watchtower
   Gain Silver to top of deck
I'd be inclined to believe it should be possible, but the code I suggested above doesn't allow it.

The problem is for the FAQ to be correct, you either decide which Silver to topdeck beforehand, but only if you will gain 2 (which isn't possible since you only know if you gain 2 after you have fully resolved all triggers for the first gain), or you always topdeck the first, which leads to the above not being possible, which doesn't feel right to me.

I think we need Donald to clarify.

EDIT: Actually, I noticed Charm (which was the "somehow" above) is on buy, so I don't think there is a way to gain something else in response to the Silver gain, but I guess we still need clarification to be forward-compatible.
« Last Edit: August 13, 2016, 11:26:50 am by Watno »
Logged

Donald X.

  • Board Moderator
  • *
  • Offline Offline
  • Posts: 6364
  • Respect: +25699
    • View Profile
Re: Scripting Dominion
« Reply #16 on: August 13, 2016, 01:24:21 pm »
+2

I think we need Donald to clarify.
You gain a Silver directly to the top of your deck, then you gain a Silver that has no special destination.
Logged

Seprix

  • Adventurer
  • ******
  • Offline Offline
  • Posts: 5607
  • Respect: +3680
    • View Profile
Re: Scripting Dominion
« Reply #17 on: August 13, 2016, 03:13:34 pm »
0

Wow, all of this code. I can kind of sort of understand it a little bit.
Logged
DM me for ideas on a new article, either here or on Discord (I check Discord way more often)

drsteelhammer

  • Torturer
  • *****
  • Offline Offline
  • Posts: 1527
  • Shuffle iT Username: drsteelhammer
  • Respect: +1471
    • View Profile
Re: Scripting Dominion
« Reply #18 on: August 13, 2016, 03:13:43 pm »
+3

anyone taking bets whether that is implemented correctly on the MF client? odds are 25 to 1
Logged
Join the Dominion League!

There is no bad shuffle that can not be surmounted by scorn.

Davio

  • 2012 Dutch Champion
  • *
  • Offline Offline
  • Posts: 4787
  • Respect: +3412
    • View Profile
Re: Scripting Dominion
« Reply #19 on: August 14, 2016, 02:02:23 am »
0

I notice Margrave does not have an "Attack" tag anywhere (in contrast to Beggar, which extends ReactionCard) - I'm assuming the fact that it has a "victim" means you resolve Attack triggers when victims are involved?  Are Masquerade and Possession dealt with differently, then?
We have the card types assigned to the card names. Margrave knows it's called CardObjectName.MARGRAVE, and it can deduce it's an attack that way.
If I would replace the name with CardObjectName.SMITHY, it would still function as a Margrave, except that nobody could defend against it (and the client would show you an image of smithy)

By the time we get to the 'victim' part it would be too late. You decide whether to reveal moat before the attacking player draws cards.
The whole possibly-removing-victims has already happened by the time Margraves onPlay is executed.
I would have gone with something like interfaces to specify card types, but then you get things like Band of Misfits and Inheritance and adding interfaces during runtime isn't going to work.

So a runtime lookup of the actual types (through card name, which might change for BoM?) is probably the more flexible option.
Logged

BSG: Cagprezimal Adama
Mage Knight: Arythea

michaeljb

  • Saboteur
  • *****
  • Offline Offline
  • Posts: 1422
  • Shuffle iT Username: michaeljb
  • Respect: +2114
    • View Profile
Re: Scripting Dominion
« Reply #20 on: August 14, 2016, 04:19:58 am »
0

Yup that would exactly be it. I would happily add you to the list of alpha-testers, except you were already on it.

I think the correct code should be simply

Code: [Select]
player.gain(context, getMe(), CardObjectName.SILVER, player.getDrawPile());
player.gain(context, getMe(), CardObjectName.SILVER);

I think we need Donald to clarify.
You gain a Silver directly to the top of your deck, then you gain a Silver that has no special destination.

Sounds like my and Stef's interpretation of Beggar's text was a little off then.

Soooo....Watno for alpha testing?
Logged
🚂 Give 18xx games a chance 🚂

Watno

  • Margrave
  • *****
  • Offline Offline
  • Posts: 2745
  • Shuffle iT Username: Watno
  • Respect: +2983
    • View Profile
Re: Scripting Dominion
« Reply #21 on: August 14, 2016, 09:25:25 am »
0

anyone taking bets whether that is implemented correctly on the MF client? odds are 25 to 1
I guess the chances it's correctly implemented is pretty high, since the correct implementation is a lot simpler than the incorrect one.

The checking name to determine type thing is problematic I think, since it is possible that there are multiple cards that have the same name, but different types: in particular, some, but not all estates might be attacks (if someone inherited an attack)
Logged

Watno

  • Margrave
  • *****
  • Offline Offline
  • Posts: 2745
  • Shuffle iT Username: Watno
  • Respect: +2983
    • View Profile
Re: Scripting Dominion
« Reply #22 on: September 13, 2016, 05:47:35 am »
0

...

Yup that would exactly be it. I would happily add you to the list of alpha-testers, except you were already on it.
We are currently aiming for a closed alpha at the start of September.
Hopefully it goes well and we can expand the list a bit halfway through September.

Any news about this?
Logged

Chris is me

  • Margrave
  • *****
  • Offline Offline
  • Posts: 2745
  • Shuffle iT Username: Chris is me
  • What do you want me to say?
  • Respect: +3458
    • View Profile
Re: Scripting Dominion
« Reply #23 on: September 13, 2016, 07:55:01 am »
+2

...

Yup that would exactly be it. I would happily add you to the list of alpha-testers, except you were already on it.
We are currently aiming for a closed alpha at the start of September.
Hopefully it goes well and we can expand the list a bit halfway through September.

Any news about this?

Ha! Joke's on you! They never said which September they were opening up Alpha testing. :)
Logged
Twitch channel: http://www.twitch.tv/chrisisme2791

bug me on discord

pm me if you wanna do stuff for the blog

they/them

Accatitippi

  • Saboteur
  • *****
  • Offline Offline
  • Posts: 1153
  • Shuffle iT Username: Accatitippi
  • Silver is underraided
  • Respect: +1797
    • View Profile
Re: Scripting Dominion
« Reply #24 on: September 13, 2016, 08:03:41 am »
+3

...

Yup that would exactly be it. I would happily add you to the list of alpha-testers, except you were already on it.
We are currently aiming for a closed alpha at the start of September.
Hopefully it goes well and we can expand the list a bit halfway through September.

Any news about this?

Ha! Joke's on you! They never said which September they were opening up Alpha testing. :)

...and in an unexpected twist of events, they found out that SCSN had been Goko all along.
Logged
Pages: [1] 2 3  All
 

Page created in 0.143 seconds with 21 queries.