fpclass / perpetual-haskelling-initiative

Main repository for the CS141 Perpetual Haskelling Initiative
MIT License
0 stars 0 forks source link

Programmable cards #13

Open ghost opened 4 years ago

ghost commented 4 years ago

From what I understood, the initial idea was to have programmable cards that the players would program themselves, however people have been coming up with ideas for specific cards. Has the idea of the programmable cards been changed or have I missed something?

If we are still having the programmable cards, we could do it where each deck only has a few cards the user programs themselves (maybe 3?). This would allow people to still have the card ideas and maybe the programmable cards could be seen as special cards which are stronger than average and can help bring a deck concept together.

lynchgeorge commented 4 years ago

I must admit I had forgotten about this so it was definitely worth asking. That seems do-able. Maybe this is worth implementing after we have some basic decks in the game?

mbg commented 4 years ago

I think the programmable cards aspect was the main idea that was agreed on the Slack, so we should prioritise that over decks with pre-made cards IMHO.

ghost commented 4 years ago

Then we need to maybe implement a base set of functions that the programmer has access too, maybe improve or modify them for different expansions such as Haskell will not be able to use random etc... . They can be written in lambda expressions but we need to define the structure or effect if we want to allow other cards to 'react' appropriately and classify them for ease of use. (IMO this comes after having a clear set of functions for the game to work) e.g one may have an on heal effect you need to call the dedicated heal effect given the state of the board that could modify it with double healing or reverse healing cards that have existed in games like hearthstone. Also other states like triggering on other turns or for the rest of the game such as [crystal core](crystal core) does once completed. The difficulty I see is user friendliness so maybe we limit the lambdas they have access to or the amount / complexity of them.

ghost commented 4 years ago

If we want this then I believe it is best to have a document that defines the interface of usable functions along with their types, this would help development and probably set us up for card design and will be used in the design of the game mechanics itself. If we prioritise this the rest should hopefully fall into place (and this also seems like a good way of planning the functions) 👍 .

ghost commented 4 years ago

As an idea of how this is probably gonna work the cards themselves would be sequences of do statements to allow the use of then using the example Earthquake from hearthstone, the reason being that one allows abilities to trigger in a different way as opposed to simply dealing 7 damage overall. This is probably best to follow a stack methodology of execution to resolve actions in an appropriate manner with cases such as counterspells. Some cards will have to update their state based on conditions of the field however the application has to be carefully managed, in some cases a spell may cause a minion summon or effect that has spell damage based on the wording of the card "THEN" would allow the state of the board to resolve and update the effect whereas in hearthstone and mtg the effect are resolved in LIFO (stack).

This as a potential start of the the programming interface a card is a non empty sequence of actions, this would be through the use of do in Haskell because targeting itself may be an action or a parameter to later be discussed. There are cases of effects that trigger spells with random targets thus it would override the effect of a card targeting or somehow copy a card with a random selector in place? Other possibilities such as removal or a target should end a spells effect e.g deal 20 damage then 10 damage if killing an entity should resolve fully killing it and ignoring the future target due to being dead (this brings up the case of how to define targeting between states if a minion is moved / killed?) maybe outside of front interface the functions pass in a sequences of indexes that are returned after each function call so. Functions will likely need to return a large amount of data about their actions for future card info e.g when healing adding "if target was healed by 2 or more then summon a copy" meaning the actual healing given must be returned. The definition of standard functions such as heal, combat etc... allows us to easily program cards to respond to that such as adding a sequence of defined events to respond to where one may be a heal event on itself for example as well as possibly creating a converter to natural language :).

Entity use in target is Entity/index+board?, board state needs to be passed at some point to return updated state but if index moved how to reassign?

Entity = Hero | Minion targetEntity :: (Entity -> Boolean) -> Some IO board selection -> Entity targetEntityFriendlyOnly :: Entity -> Boolean targetEntityMinionType :: Type -> Minion -> Boolean

heal :: Int -> [Entity] -> newState

myCard = do t, s1 <- targetEntity targetEntityFriendlyOnly s2 <- heal 10 [t] return s2

copyCardRandom = do t, s1 <- targetCard targetCardFriendlyOnly return runRandomiser t

The above is just some pseudo rambling about how I think it would be done but I feel like I am making this too complicated, what is your opinions on this?

oscar-h64 commented 4 years ago

I think we definitely need to plan out possible functions before we add them. Currently we have a basic set of instructions with the assembly language (#6 and #21), but we need to add more really, and we'd have to add a check to make sure the actions are valid for that paradigm.

I think the rest of that refers to the processMove function. We are storing the state as a type called Board, which contains 2 Player types and which players turn it is, so I'd guess most actions will take and return either a Player or a Board. I agree with treating the action like a stack.

I may have completely misinterpreted what you're saying, if so just say

ghost commented 4 years ago

Possibly write the functions using linear types? This seems safest but may be easier to use case analysis of data where currently stored variables are parsed in and returned to update state between interactions or commands in assembly.

Ayarjay commented 4 years ago

that seems like the best method of applying the events, I believed a stack hierarchy was agreed to have been used. Each event would need a pre-event, the event itself that is a function of the events and passed in arguments and any post-event trigger, the language would be best to use a mixture of conditional events based on current state using boolean logic and first order logic sequences with some potential to apply in parallel. If the system also has events that can be applied globally then it makes sense to use this as the basis of the game.

Ayarjay commented 4 years ago

e.g some minions could redirect damage (pre), which would affect the damage event itself and maybe trigger another event by calling damage itself on another target. The post damage event would be an instance such as "when this minion is damaged do ___".