Skip to content

Commit

Permalink
Big updates in the design coming. Sketches are prototyped in `DesignS…
Browse files Browse the repository at this point in the history
…ketch.hs`.
  • Loading branch information
BlastWind committed Oct 14, 2024
1 parent 61009d4 commit 30c356d
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 8 deletions.
3 changes: 2 additions & 1 deletion battlegrounds.cabal
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cabal-version: 2.2

-- This file has been generated from package.yaml by hpack version 0.36.0.
-- This file has been generated from package.yaml by hpack version 0.37.0.
--
-- see: https://github.com/sol/hpack

Expand Down Expand Up @@ -28,6 +28,7 @@ library
Card
Combat
Controller
DesignSketch
Logic
Model
Utils
Expand Down
4 changes: 3 additions & 1 deletion src/Model.md → docs/Model.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ updateCard :: Card -> Card
upgradeScope :: (Card -> Card) -> (GameState -> GameState)
```

The `zoom` lens allows us to write this efficiently.

### Guiding Questions
#### Modelling `Malchezaar`, `Seafarer`, `Bazaar Dealer`, `Chimera`, `Octosari`, `Felboar`, `Elise`?

The named minions are have special effects that also have the notion of "counters" to them.
These minions are have special effects that also have the notion of "counters" to them.

First, I write some algebraic laws:
```haskell
Expand Down
30 changes: 24 additions & 6 deletions src/Thoughts.md → docs/Thoughts.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ to issue commands to the server, etc.
Note, server and client will be bidirectionally. Server needs to manage phase timers and ping the client when phase changes.
So, the client needs to `forkIO` twice for a sending and receiving thread. The server can have one `fork` per client.

### Aug 1, 2024
### Aug 1, 2024: Encapsulate custom effects in callbacks
Callbacks can be used to implement both in-game logic and game rules. For example, omu's hero power can be implemented via a callback
to `tierUp`. Conversely, maybe `tierUp` can expand the `randomShopSize` via a callback, as opposed to this being built-in. For now,
`randomShop` is directly dependent on `TavernTier`, but maybe that can change.

### Aug 4, 2024
### Aug 4, 2024: `simulateCombat` logic
Taming `simulateCombat`. A few additions to make
- Allow each call of `go` add multiple snapshots to the combat history. Each `go` handles all that transpires from one player's turn, which involves
- capture initial state
- on minion attack (capture state)
- minions trade (capture state, dead minions are snapped)
- minions trade (capture state, dead minions are snapped because this should show in the UI)
- clear dead minions (capture state)
- trigger deathrattles (capture state)

Expand All @@ -38,20 +38,38 @@ Today's goal:
I think since both Onyxia's whelp and Scallywag have the "attack immediately" keyword, it warrants a `AttackImmediately` keyword.
When a minion with `AttackImmediately` is summoned, it is to perform an attack.

### Aug 5, 2024
### Aug 5, 2024: How to write tests when randomness is invovled.
How can I write tests?

They need to fulfill two requirements:
- Every thing that could be random may be controlled
- If unspecified, choices fall back to random sourcing
- Randomness are specified exactly where they are needed.

### Aug 6, 2024
### Aug 6, 2024: Initial steps to handling randomness
To answer 8/5 questions: Write a full DSL and interpreter for all of the card effects.
During testing, override terms like `RandomIndex` to `SpecificIndex`.

Refactoring: The API calls for many redesign opprotunities:
1. Since `FighterState` provides `PlayerState`, no need for `combatBoard`.
2. Need to seriously think about the pros and cons of the different CombatState designs.

Today's result: Combat.hs typechecks. Claude suggests to refactor `CombatState` into a map of `{ One: one's fighterstate, Two: two's fighterstate }`
Today's result: Combat.hs typechecks. Claude suggests to refactor `CombatState` into a map of `{ One: one's fighterstate, Two: two's fighterstate }`

### Oct 13, 2024:
Been reading Granin's FDD book. Main question I'm having:
1. What types can be "correct by construction"?


I wasn't able to get far from this question. But, I started a `Temp.hs` file in which I started mocking things in. This has proven to be such a great exercise — it's great seeing just how compositional some effects are.


****

Questions from 8/5/2024 still persist:
- How to determine everything random in tests?
- If unspecified, choices fall back to random sourcing

Thought:

Another question:
127 changes: 127 additions & 0 deletions src/DesignSketch.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
-- Temp file. Place for some all-in-one mocks

module TempDesignSketch (module TempDesignSketch) where

Check failure on line 3 in src/DesignSketch.hs

View workflow job for this annotation

GitHub Actions / build

File name does not match module name:

-- Sources of randomness:
-- 1. Random defender selection
-- 2. Random shop rolls
-- 3. Choose random minion in tavern (picky eater, enchanted lasso)
-- 4. Choose random minion in opponent's warband (cry foul)
-- 4. Choose random card under some additional criterion (chef's choice, shell whistler)

type Multiplier = Int

data RandomTarget
= HandRandom
| HandLeftmost
| HandRightmost
| BoardRandom
| ShopRandom
deriving (Eq)

data RandomCriterion = RTribe Tribe | RTarget RandomTarget | RTier Int deriving (Eq)

data CounterCriterion
= -- Upbeat Frontdrake
EndOfTurn
| -- Avenge mechanic
FriendlyDeaths
deriving (Eq)

-- Unused yet.
data Target
= TShop Int
| THand Int
-- Them Apples.
| TEntireShop

data TargetedEffect -- TODO: The idea of a target might be important.

data StateEffect
= -- E.g., Glim Guardian
GainStats Int Int
| -- E.g., Cord Puller
Summon CardName
| -- E.g., Backstage Security
DamageHero Int
| -- E.g., Upbeat Frontdrake
GetRandom [RandomCriterion]
| -- E.g., Geomancer
Get CardName
| -- E.g., Picky Eater
Consume Multiplier
| -- E.g., Tavern Coin
GainGold Int
deriving (Eq)

data Tribe = Murloc | Dragon | Demon deriving (Eq)

-- TODO: It has became obvious what are "keywords" and what are "functionalities". Just observe who has StateEffect.
data Functionality
= -- phase=Combat
Taunt
| DivineShield
| Deathrattle [StateEffect]
| OnAttack [StateEffect]
| OnDamaged [StateEffect]
| OnKill [StateEffect]
| OnSummon [StateEffect]
| OnSell [StateEffect]
| StartOfCombat
| Reborn
| Windfury
| -- phase=Recruit
Battlecry StateEffect
| AfterPlay [StateEffect]
| Spellcraft Card
| -- Both
AfterSummon Tribe StateEffect
| -- Combinator
Counter CounterCriterion Int StateEffect
deriving (Eq)

data Stats = Stats Int Int deriving (Eq)

data CardName = GlimGuardian | HarmlessBonehead | CordPuller | Skeleton | Microbot | UpbeatFrontdrake | EnchantedLasso deriving (Eq)

data Card = Card CardName Stats [Functionality] deriving (Eq)

newtype CardInstance = CardInstance Card

glimGuardian :: Card
glimGuardian = Card GlimGuardian (Stats 1 4) [OnAttack [GainStats 2 1]]

skeleton :: Card
skeleton = Card Skeleton (Stats 1 1) []

harmlessBonehead :: Card
harmlessBonehead = Card HarmlessBonehead (Stats 1 1) [Deathrattle [Summon Skeleton, Summon Skeleton]]

microbot :: Card
microbot = Card Microbot (Stats 1 1) []

cordPuller :: Card
cordPuller = Card CordPuller (Stats 1 1) [DivineShield, Deathrattle [Summon Microbot]]

upbeatFrontdrake :: Card
upbeatFrontdrake = Card UpbeatFrontdrake (Stats 1 1) [Counter EndOfTurn 3 (GetRandom [RTribe Dragon])]

-- Look mom! Tavern spells can be modeled as cards.
enchantedLasso :: Card
enchantedLasso = Card EnchantedLasso (Stats 0 0) [Battlecry (GetRandom [RTarget ShopRandom])]

glimGuardianInst :: CardInstance
glimGuardianInst = CardInstance glimGuardian

data GameState
= GameState
{ p1 :: [Card],
p2 :: [Card],
onSummonCallbacks :: [GameState -> GameState],
onPlayCallbacks :: [GameState -> GameState]
}

trade :: (CardInstance, CardInstance) -> (CardInstance, CardInstance)
trade (CardInstance (Card name1 stats1 fns1), CardInstance (Card name2 stats2 fns2)) = (_, _)
where
ds1 = DivineShield `elem` fns1

0 comments on commit 30c356d

Please sign in to comment.