Compare commits
26 Commits
refactor-p
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
18b221d972 | ||
|
e45c5b3711 | ||
f53beb4b17 | |||
c30254a36d | |||
350fd0f777 | |||
816b5e8e7a | |||
e61d7571f0 | |||
6d6d0f5c8b | |||
3ca17d8881 | |||
976610b1bb | |||
a4692712cc | |||
2ca8f3ed13 | |||
0a39cc76d6 | |||
de7092cf4b | |||
d434e50897 | |||
95a30cb522 | |||
f3fd0c582f | |||
de98cf8fb3 | |||
d66e17a279 | |||
caa5c2e60c | |||
159508b202 | |||
301d8ae161 | |||
e0dad09045 | |||
ccd141ddc5 | |||
d2f89f5bd4 | |||
f7bed6c4b9 |
9
go.mod
9
go.mod
@ -2,4 +2,11 @@ module git.chromaticdragon.app/kistaro/KoboldSim
|
|||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require git.chromaticdragon.app/kistaro/CardSimEngine v0.1.3
|
require git.chromaticdragon.app/kistaro/CardSimEngine v0.5.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/kr/pretty v0.3.1 // indirect
|
||||||
|
github.com/kr/text v0.2.0 // indirect
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
|
||||||
|
)
|
||||||
|
16
go.sum
16
go.sum
@ -1,4 +1,12 @@
|
|||||||
git.chromaticdragon.app/kistaro/CardSimEngine v0.1.2 h1:+8KVFhSxXbQO7CPzmL89sJ+qjgU4J42Z5OinGuDMR0U=
|
git.chromaticdragon.app/kistaro/CardSimEngine v0.5.0 h1:o4ncTVfDgax3w2tVhkap4/ZrZDe13YnNNJ0pe7zaqjM=
|
||||||
git.chromaticdragon.app/kistaro/CardSimEngine v0.1.2/go.mod h1:VFaOagdbtM6gH87ioHent8v76nDh9PddpymMqWdrLfI=
|
git.chromaticdragon.app/kistaro/CardSimEngine v0.5.0/go.mod h1:FYuoJHaK7lDI8Fwf4lZY2Y+8P9zavT4oLvSFUG6drw4=
|
||||||
git.chromaticdragon.app/kistaro/CardSimEngine v0.1.3 h1:rNaDDXnPVoMpavpx4vR9k30sl0nO0BnCe32nZbAF2IM=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
git.chromaticdragon.app/kistaro/CardSimEngine v0.1.3/go.mod h1:VFaOagdbtM6gH87ioHent8v76nDh9PddpymMqWdrLfI=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
|
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
|
||||||
|
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||||
|
1116
koboldsim/cards.go
1116
koboldsim/cards.go
File diff suppressed because it is too large
Load Diff
@ -42,12 +42,62 @@ type Policy interface {
|
|||||||
// previously, and undoes it before doing a different one. It is always valid
|
// previously, and undoes it before doing a different one. It is always valid
|
||||||
// to draw.
|
// to draw.
|
||||||
type SwitchingCard struct {
|
type SwitchingCard struct {
|
||||||
|
// Name contains the name of the card, displayed as its title in the
|
||||||
|
// action selection menu and in the card detail page itself.
|
||||||
Name cardsim.Message
|
Name cardsim.Message
|
||||||
|
|
||||||
|
// Desc contains the event description for the card, displayed in the
|
||||||
|
// card detail page.
|
||||||
Desc cardsim.Message
|
Desc cardsim.Message
|
||||||
|
|
||||||
|
// IsUrgent marks a card as urgent. If the player has any urgent cards
|
||||||
|
// in hand, they cannot act on any non-urgent cards (or permanent actions
|
||||||
|
// not marked as urgent).
|
||||||
IsUrgent bool
|
IsUrgent bool
|
||||||
|
|
||||||
|
// After is invoked after the player has chosen to act on this card and
|
||||||
|
// the chosen option has been fully enacted. If the card should be returned
|
||||||
|
// to the deck, After is responsible for doing this!
|
||||||
|
//
|
||||||
|
// If After is not provided, ShuffleIntoBottomHalf is used as a fallback.
|
||||||
|
//
|
||||||
|
// The first argument to After is the card itself. This will be type
|
||||||
|
// *SwitchingCard. It's represented as Card so general "After" functions
|
||||||
|
// that can be used with multiple card types (for example,
|
||||||
|
// ShuffleIntoBottomHalf) can be trivially implemented.
|
||||||
|
//
|
||||||
|
// If the card cannot be drawn into the hand because it has an IsValid
|
||||||
|
// check and the check fails, After is invoked with a nil CardOption.
|
||||||
|
// ShuffleIntoBottomHalf works just fine with a nil argument here.
|
||||||
|
// (It doesn't care about the CardOption at all.)
|
||||||
After func(Card, *Player, CardOption) error
|
After func(Card, *Player, CardOption) error
|
||||||
|
|
||||||
|
// IsValid is used to check whether the card can be drawn into the hand.
|
||||||
|
// If it cannot, After is immediately invoked (whenever the card was
|
||||||
|
// being drawn, which is probably the "draw" stage of the turn but can
|
||||||
|
// happen any time if something else causes the player to draw cards) with
|
||||||
|
// a nil CardOption (because no option was selected) and the SwitchingCard
|
||||||
|
// does not invoke any option and does not change its active policy.
|
||||||
|
//
|
||||||
|
// The first argument to IsValid is the card itself. This will be type
|
||||||
|
// *SwitchingCard. It's presented via the Card interface to support
|
||||||
|
// general validity functions that could be used with arbitrary kinds of Card.
|
||||||
|
IsValid func(Card, *Player) bool
|
||||||
|
|
||||||
|
// Policies contains the options the player may choose between. Policy is
|
||||||
|
// a more specific type than CardOption; a Policy can be un-enacted.
|
||||||
|
// Unenactment of the previous policy before selecting a new one is the
|
||||||
|
// core feature of SwitchingCard.
|
||||||
Policies []Policy
|
Policies []Policy
|
||||||
|
|
||||||
|
// lastPolicy stores the last policy selected for this card. It's used
|
||||||
|
// extensively by SwitchingCard's logic.
|
||||||
lastPolicy Policy
|
lastPolicy Policy
|
||||||
|
|
||||||
|
// ShowUnavailable controls whether options for which Enabled() = false
|
||||||
|
// should be presented to the player at all. Indexes for "last enacted"
|
||||||
|
// still refer to the original list, not the shortened one.
|
||||||
|
ShowUnavailable bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Title implements Card.
|
// Title implements Card.
|
||||||
@ -61,7 +111,12 @@ func (s *SwitchingCard) Urgent(*Player) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Drawn implements Card.
|
// Drawn implements Card.
|
||||||
func (s *SwitchingCard) Drawn(*Player) bool {
|
func (s *SwitchingCard) Drawn(p *Player) bool {
|
||||||
|
if s.IsValid != nil && !s.IsValid(s, p) {
|
||||||
|
err := s.Then(p, nil)
|
||||||
|
p.ReportError(err) // can't do anything with the error right now
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +126,7 @@ func (s *SwitchingCard) EventText(*Player) (cardsim.Message, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Options implements Card.
|
// Options implements Card.
|
||||||
func (s *SwitchingCard) Options(*Player) ([]CardOption, error) {
|
func (s *SwitchingCard) Options(player *Player) ([]CardOption, error) {
|
||||||
lastIdx := -1
|
lastIdx := -1
|
||||||
for i, p := range s.Policies {
|
for i, p := range s.Policies {
|
||||||
if p.Is(s.lastPolicy) {
|
if p.Is(s.lastPolicy) {
|
||||||
@ -79,18 +134,21 @@ func (s *SwitchingCard) Options(*Player) ([]CardOption, error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret := make([]CardOption, len(s.Policies))
|
ret := make([]CardOption, 0, len(s.Policies))
|
||||||
for i, p := range s.Policies {
|
for _, p := range s.Policies {
|
||||||
p.LastEnacted(lastIdx, s.lastPolicy)
|
p.LastEnacted(lastIdx, s.lastPolicy)
|
||||||
ret[i] = p
|
if s.ShowUnavailable || p.Enabled(player) {
|
||||||
|
ret = append(ret, p)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then implements Card.
|
// Then implements Card.
|
||||||
func (s *SwitchingCard) Then(p *Player, o CardOption) error {
|
func (s *SwitchingCard) Then(p *Player, o CardOption) error {
|
||||||
newPolicy := o.(Policy)
|
|
||||||
var errs cardsim.ErrorCollector
|
var errs cardsim.ErrorCollector
|
||||||
|
if o != nil {
|
||||||
|
newPolicy := o.(Policy)
|
||||||
if s.lastPolicy != nil && !newPolicy.Is(s.lastPolicy) {
|
if s.lastPolicy != nil && !newPolicy.Is(s.lastPolicy) {
|
||||||
err := s.lastPolicy.Unenact(p)
|
err := s.lastPolicy.Unenact(p)
|
||||||
if cardsim.IsSeriousError(err) {
|
if cardsim.IsSeriousError(err) {
|
||||||
@ -99,12 +157,22 @@ func (s *SwitchingCard) Then(p *Player, o CardOption) error {
|
|||||||
errs.Add(err)
|
errs.Add(err)
|
||||||
}
|
}
|
||||||
s.lastPolicy = o.(Policy)
|
s.lastPolicy = o.(Policy)
|
||||||
|
}
|
||||||
if s.After != nil {
|
if s.After != nil {
|
||||||
errs.Add(s.After(s, p, o))
|
errs.Add(s.After(s, p, o))
|
||||||
|
} else {
|
||||||
|
// Fallback: Shuffle the card back into the bottom half of the deck.
|
||||||
|
errs.Add(ShuffleIntoBottomHalf(s, p, o))
|
||||||
|
|
||||||
}
|
}
|
||||||
return errs.Emit()
|
return errs.Emit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CurrentlyEnacted returns the currently enacted Policy, if any.
|
||||||
|
func (s *SwitchingCard) CurrentlyEnacted() Policy {
|
||||||
|
return s.lastPolicy
|
||||||
|
}
|
||||||
|
|
||||||
// BasicPolicy is a straightfoward implementation of Policy. If the currently
|
// BasicPolicy is a straightfoward implementation of Policy. If the currently
|
||||||
// enacted option is re-enacted, it refunds the player's action point.
|
// enacted option is re-enacted, it refunds the player's action point.
|
||||||
type BasicPolicy struct {
|
type BasicPolicy struct {
|
||||||
@ -114,25 +182,29 @@ type BasicPolicy struct {
|
|||||||
NothingChanged cardsim.Message
|
NothingChanged cardsim.Message
|
||||||
Do func(*Player) (cardsim.Message, error)
|
Do func(*Player) (cardsim.Message, error)
|
||||||
Undo func(*Player) error
|
Undo func(*Player) error
|
||||||
CanDo func(*Player) bool
|
CanDo func(*BasicPolicy, *Player) bool
|
||||||
|
|
||||||
currentlyEnacted bool
|
CurrentlyEnacted bool
|
||||||
|
LastEnactedPolicy Policy
|
||||||
|
LastEnactedIdx int
|
||||||
}
|
}
|
||||||
|
|
||||||
// YesWeCan returns true. It's the default value for BasicPolicy.CanDo / BasicPolicy.CanUndo.
|
// YesWeCan returns true. It's the default value for BasicPolicy.CanDo / BasicPolicy.CanUndo.
|
||||||
func YesWeCan(*Player) bool {
|
func YesWeCan(*BasicPolicy, *Player) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// LastEnacted notifies b about the last-enacted policy in its group. It updates
|
// LastEnacted notifies b about the last-enacted policy in its group. It updates
|
||||||
// b.currentlyEnacted accordingly.
|
// b.currentlyEnacted accordingly.
|
||||||
func (b *BasicPolicy) LastEnacted(_ int, p Policy) {
|
func (b *BasicPolicy) LastEnacted(i int, p Policy) {
|
||||||
b.currentlyEnacted = b.Is(p)
|
b.LastEnactedPolicy = p
|
||||||
|
b.LastEnactedIdx = i
|
||||||
|
b.CurrentlyEnacted = b.Is(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OptionText implements CardOption.
|
// OptionText implements CardOption.
|
||||||
func (b *BasicPolicy) OptionText(*Player) (cardsim.Message, error) {
|
func (b *BasicPolicy) OptionText(*Player) (cardsim.Message, error) {
|
||||||
if b.currentlyEnacted {
|
if b.CurrentlyEnacted {
|
||||||
if b.EnactedDesc == nil {
|
if b.EnactedDesc == nil {
|
||||||
return nil, ErrUnimplemented
|
return nil, ErrUnimplemented
|
||||||
}
|
}
|
||||||
@ -149,22 +221,19 @@ func (b *BasicPolicy) Enact(p *Player) (cardsim.Message, error) {
|
|||||||
if b.Do == nil {
|
if b.Do == nil {
|
||||||
return nil, ErrUnimplemented
|
return nil, ErrUnimplemented
|
||||||
}
|
}
|
||||||
if b.currentlyEnacted {
|
if b.CurrentlyEnacted {
|
||||||
p.ActionsRemaining++
|
p.ActionsRemaining++
|
||||||
if b.NothingChanged == nil {
|
if b.NothingChanged == nil {
|
||||||
b.NothingChanged = cardsim.MsgStr("You continue your current approach.")
|
b.NothingChanged = cardsim.MsgStr("You continue your current approach.")
|
||||||
}
|
}
|
||||||
return b.NothingChanged, nil
|
return b.NothingChanged, nil
|
||||||
}
|
}
|
||||||
if b.Enabled(p) {
|
|
||||||
return b.Do(p)
|
return b.Do(p)
|
||||||
}
|
}
|
||||||
return nil, ErrOptionNotEnabled
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unenact implements Policy.
|
// Unenact implements Policy.
|
||||||
func (b *BasicPolicy) Unenact(p *Player) error {
|
func (b *BasicPolicy) Unenact(p *Player) error {
|
||||||
if !b.currentlyEnacted {
|
if !b.CurrentlyEnacted {
|
||||||
return ErrPolicyNotEnacted
|
return ErrPolicyNotEnacted
|
||||||
}
|
}
|
||||||
if b.Undo == nil {
|
if b.Undo == nil {
|
||||||
@ -175,13 +244,13 @@ func (b *BasicPolicy) Unenact(p *Player) error {
|
|||||||
|
|
||||||
// Enabled implements CardOption.
|
// Enabled implements CardOption.
|
||||||
func (b *BasicPolicy) Enabled(p *Player) bool {
|
func (b *BasicPolicy) Enabled(p *Player) bool {
|
||||||
if b.currentlyEnacted {
|
if b.CurrentlyEnacted {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if b.CanDo == nil {
|
if b.CanDo == nil {
|
||||||
panic(ErrUnimplemented)
|
panic(ErrUnimplemented)
|
||||||
}
|
}
|
||||||
return b.CanDo(p)
|
return b.CanDo(b, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BasicPolicy) Is(p Policy) bool {
|
func (b *BasicPolicy) Is(p Policy) bool {
|
||||||
@ -195,14 +264,14 @@ func (b *BasicPolicy) Is(p Policy) bool {
|
|||||||
// policy. Which policy is used is determined by what the previous policy for
|
// policy. Which policy is used is determined by what the previous policy for
|
||||||
// the card was (as reported via a call to LastEnacted):
|
// the card was (as reported via a call to LastEnacted):
|
||||||
//
|
//
|
||||||
// * If no policy has yet been enacted, use FirstTime.
|
// - If no policy has yet been enacted, use FirstTime.
|
||||||
// * If a policy has been enacted, use the Policy at the slot in Variants
|
// - If a policy has been enacted, use the Policy at the slot in Variants
|
||||||
// that corresponds to the slot (on the Card) of the currently-enacted policy.
|
// that corresponds to the slot (on the Card) of the currently-enacted policy.
|
||||||
// * If the policy retrieved in this way returns ErrUnimplemented, throw away
|
// - If the policy retrieved in this way returns ErrUnimplemented, throw away
|
||||||
// its response and use Default instead. For Enabled, which does not have
|
// its response and use Default instead. For Enabled, which does not have
|
||||||
// an error component to its return value, look for ErrUnimplemented as the
|
// an error component to its return value, look for ErrUnimplemented as the
|
||||||
// argument to a Panic call, instead.
|
// argument to a Panic call, instead.
|
||||||
// * If the policy retrieved in this way returns ErrKeepMessage when Enact
|
// - If the policy retrieved in this way returns ErrKeepMessage when Enact
|
||||||
// is called, it calls Default for the side effects but ignores its message,
|
// is called, it calls Default for the side effects but ignores its message,
|
||||||
// retaining the message from the original call. This is to avoid having to
|
// retaining the message from the original call. This is to avoid having to
|
||||||
// repeat the same Enact function except with different text each time.
|
// repeat the same Enact function except with different text each time.
|
||||||
@ -257,7 +326,7 @@ func (v *VerbosePolicy) fillDefaults() {
|
|||||||
for len(v.Variants) <= v.lastIdx {
|
for len(v.Variants) <= v.lastIdx {
|
||||||
v.Variants = append(v.Variants, v.Default)
|
v.Variants = append(v.Variants, v.Default)
|
||||||
}
|
}
|
||||||
if v.lastIdx > 0 && v.Variants[v.lastIdx] == nil {
|
if v.lastIdx >= 0 && v.Variants[v.lastIdx] == nil {
|
||||||
v.Variants[v.lastIdx] = v.Default
|
v.Variants[v.lastIdx] = v.Default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
package koboldsim
|
package koboldsim
|
||||||
|
|
||||||
import "git.chromaticdragon.app/kistaro/CardSimEngine/cardsim"
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.chromaticdragon.app/kistaro/CardSimEngine/cardsim"
|
||||||
|
)
|
||||||
|
|
||||||
func InitPlayer() *Player {
|
func InitPlayer() *Player {
|
||||||
p := cardsim.InitPlayer(NewKoboldMine())
|
p := cardsim.InitPlayer(NewKoboldMine())
|
||||||
@ -13,6 +17,16 @@ func InitPlayer() *Player {
|
|||||||
Name: cardsim.MsgStr("All Stats"),
|
Name: cardsim.MsgStr("All Stats"),
|
||||||
Intro: cardsim.MsgStr("All available statistics."),
|
Intro: cardsim.MsgStr("All available statistics."),
|
||||||
},
|
},
|
||||||
|
&cardsim.BasicStatsPanel[*KoboldMine]{
|
||||||
|
Name: cardsim.MsgStr("Per Capita Economic"),
|
||||||
|
Intro: cardsim.MsgStr("Yield and Investment per Capita"),
|
||||||
|
Filter: cardsim.All(
|
||||||
|
cardsim.VisibleOrDebug[*KoboldMine],
|
||||||
|
func(p *Player, s cardsim.Stat) bool {
|
||||||
|
return strings.Contains(s.StatName(), "Productivity") || strings.Contains(s.StatName(), "Investment")
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
p.Prompt = &cardsim.BasicStatsPanel[*KoboldMine]{
|
p.Prompt = &cardsim.BasicStatsPanel[*KoboldMine]{
|
||||||
Name: cardsim.MsgStr("The Kobold Mine"),
|
Name: cardsim.MsgStr("The Kobold Mine"),
|
||||||
@ -21,6 +35,7 @@ func InitPlayer() *Player {
|
|||||||
"Kobolds",
|
"Kobolds",
|
||||||
"Total Sector Income",
|
"Total Sector Income",
|
||||||
"Total Government Expense",
|
"Total Government Expense",
|
||||||
|
"Tax Rate",
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
p.State = cardsim.GameActive
|
p.State = cardsim.GameActive
|
||||||
|
@ -1,52 +1,410 @@
|
|||||||
package koboldsim
|
package koboldsim
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
|
|
||||||
"git.chromaticdragon.app/kistaro/CardSimEngine/cardsim"
|
"git.chromaticdragon.app/kistaro/CardSimEngine/cardsim"
|
||||||
)
|
)
|
||||||
|
|
||||||
// KoboldMine is the state of a kobold mine.
|
// KoboldMine is the state of a kobold mine.
|
||||||
type KoboldMine struct {
|
type KoboldMine struct {
|
||||||
Kobolds cardsim.Stored[int64]
|
BasePopulation float64 `cardsim:"stathidden"`
|
||||||
|
|
||||||
SectorMiningIncome cardsim.Stored[float64]
|
MiningIncome float64 `cardsim:"stathidden" cardsim_name:"Mining Productivity"`
|
||||||
SectorScavengingIncome cardsim.Stored[float64]
|
ScavengingIncome float64 `cardsim:"stathidden" cardsim_name:"Scavenging Productivity"`
|
||||||
|
AlchemyIncome float64 `cardsim:"stathidden" cardsim_name:"Alchemy Productivity"`
|
||||||
|
HospitalityIncome float64 `cardsim:"stathidden" cardsim_name:"Hospitality Productivity"`
|
||||||
|
AgricultureIncome float64 `cardsim:"stathidden" cardsim_name:"Agriculture Productivity"`
|
||||||
|
ManufacturingIncome float64 `cardsim:"stathidden" cardsim_name:"Manufacturing Productivity"`
|
||||||
|
PlanarIncome float64 `cardsim:"stathidden" cardsim_name:"Planar Productivity"`
|
||||||
|
PublishingIncome float64 `cardsim:"stathidden" cardsim_name:"Publishing Productivity"`
|
||||||
|
ForestryIncome float64 `cardsim:"stathidden" cardsim_name:"Forestry Productivity"`
|
||||||
|
FinanceIncome float64 `cardsim:"stathidden" cardsim_name:"Finance Productivity"`
|
||||||
|
GadgetryIncome float64 `cardsim:"stathidden" cardsim_name:"Gadgetry Productivity"`
|
||||||
|
FishingIncome float64 `cardsim:"stathidden" cardsim_name:"Fishing Productivity"`
|
||||||
|
ConstructionIncome float64 `cardsim:"stathidden" cardsim_name:"Construction Productivity"`
|
||||||
|
|
||||||
GovBureaucracyExpense cardsim.Stored[float64]
|
PropagandaExpense float64 `cardsim:"stathidden" cardsim_name:"Propaganda Investment"`
|
||||||
GovWarExpense cardsim.Stored[float64]
|
BureaucracyExpense float64 `cardsim:"stathidden" cardsim_name:"Bureaucracy Investment"`
|
||||||
|
WarExpense float64 `cardsim:"stathidden" cardsim_name:"War Investment"`
|
||||||
|
QoLExpense float64 `cardsim:"stathidden" cardsim_name:"QoL Investment"`
|
||||||
|
LogisticsExpense float64 `cardsim:"stathidden" cardsim_name:"Logistics Investment"`
|
||||||
|
DragonSubsExpense float64 `cardsim:"stathidden" cardsim_name:"Dragon Subsidies Investment"`
|
||||||
|
ResearchSubsExpense float64 `cardsim:"stathidden" cardsim_name:"Research Subsidies Investment"`
|
||||||
|
EducationExpense float64 `cardsim:"stathidden" cardsim_name:"Education Investment"`
|
||||||
|
HealthcareExpense float64 `cardsim:"stathidden" cardsim_name:"Healthcare Investment"`
|
||||||
|
ForeignRelExpense float64 `cardsim:"stathidden" cardsim_name:"Foreign Relations Investment"`
|
||||||
|
PoliceExpense float64 `cardsim:"stathidden" cardsim_name:"Law Enforcement Investment"`
|
||||||
|
EconPlanExpense float64 `cardsim:"stathidden" cardsim_name:"Economic Planning Investment"`
|
||||||
|
ParksExpense float64 `cardsim:"stathidden" cardsim_name:"Parks and Aesthetics Investment"`
|
||||||
|
FaithExpense float64 `cardsim:"stathidden" cardsim_name:"Faith Investment"`
|
||||||
|
|
||||||
|
FoodSupply float64 `cardsim:"stathidden"`
|
||||||
|
ObesogenicFood float64 `cardsim:"stathidden"`
|
||||||
|
ForeignRelations float64 `cardsim:"stathidden"`
|
||||||
|
HiddenRelPenalty float64 `cardsim:"stathidden"` // Lower is better.
|
||||||
|
Secrecy float64 `cardsim:"stathidden"`
|
||||||
|
PointOfDimReturns float64 `cardsim:"stathidden"`
|
||||||
|
Rebellion float64 `cardsim:"stathidden"`
|
||||||
|
Madness float64 `cardsim:"stathidden"`
|
||||||
|
Cruelty float64 `cardsim:"stat"`
|
||||||
|
Greed float64 `cardsim:"stat"`
|
||||||
|
Gullibility float64 `cardsim:"stathidden"`
|
||||||
|
Authoritarianism float64 `cardsim:"stat"`
|
||||||
|
|
||||||
|
// AnotherExpense float64 `cardsim:"hiddenround5"`
|
||||||
|
// A different way of adding stats that is slightly more empowering.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *KoboldMine) ProductivityFunc(s *cardsim.Stored[float64]) func() float64 {
|
func (k *KoboldMine) ProductivityFunc(s float64) func() float64 {
|
||||||
return func() float64 {
|
return func() float64 {
|
||||||
return s.Value * float64(k.Kobolds.Value)
|
return math.Max(s*float64(k.Kobolds()), 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) ProductivityTotal() float64 {
|
||||||
|
total := math.Max(k.MiningIncome, 0.01)
|
||||||
|
total += math.Max(k.ScavengingIncome, 0)
|
||||||
|
total += math.Max(k.AlchemyIncome, 0)
|
||||||
|
total += math.Max(k.HospitalityIncome, 0)
|
||||||
|
total += math.Max(k.AgricultureIncome, 0)
|
||||||
|
total += math.Max(k.ManufacturingIncome, 0)
|
||||||
|
total += math.Max(k.PlanarIncome, 0)
|
||||||
|
total += math.Max(k.PublishingIncome, 0)
|
||||||
|
total += math.Max(k.FinanceIncome, 0)
|
||||||
|
total += math.Max(k.GadgetryIncome, 0)
|
||||||
|
total += math.Max(k.FishingIncome, 0)
|
||||||
|
total += math.Max(k.ConstructionIncome, 0.02)
|
||||||
|
total += math.Max(k.PropagandaExpense, 0)
|
||||||
|
total += math.Max(k.BureaucracyExpense, 0)
|
||||||
|
total += math.Max(k.WarExpense, 0)
|
||||||
|
total += math.Max(k.QoLExpense, 0)
|
||||||
|
total += math.Max(k.LogisticsExpense, 0)
|
||||||
|
total += math.Max(k.DragonSubsExpense, 0)
|
||||||
|
total += math.Max(k.ResearchSubsExpense, 0)
|
||||||
|
total += math.Max(k.EducationExpense, 0)
|
||||||
|
total += math.Max(k.HealthcareExpense, 0)
|
||||||
|
total += math.Max(k.ForeignRelExpense, 0)
|
||||||
|
total += math.Max(k.PoliceExpense, 0)
|
||||||
|
total += math.Max(k.EconPlanExpense, 0)
|
||||||
|
total += math.Max(k.ParksExpense, 0)
|
||||||
|
total += math.Max(k.FaithExpense, 0)
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) ProductivityMultiplier() float64 {
|
||||||
|
return math.Pow(0.95, math.Max(0, k.ProductivityTotal()-k.PointOfDimReturns))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TrueMiningIncome() float64 {
|
||||||
|
return math.Max(k.MiningIncome*k.ProductivityMultiplier(), 0.01)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TrueScavengingIncome() float64 {
|
||||||
|
return math.Max(k.ScavengingIncome*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TrueAlchemyIncome() float64 {
|
||||||
|
return math.Max(k.AlchemyIncome*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TrueHospitalityIncome() float64 {
|
||||||
|
return math.Max(k.HospitalityIncome*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TrueAgricultureIncome() float64 {
|
||||||
|
return math.Max(k.AgricultureIncome*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TrueManufacturingIncome() float64 {
|
||||||
|
return math.Max(k.ManufacturingIncome*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TruePlanarIncome() float64 {
|
||||||
|
return math.Max(k.PlanarIncome*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TruePublishingIncome() float64 {
|
||||||
|
return math.Max(k.PublishingIncome*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TrueForestryIncome() float64 {
|
||||||
|
return math.Max(k.ForestryIncome*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TrueFinanceIncome() float64 {
|
||||||
|
return math.Max(k.FinanceIncome*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TrueGadgetryIncome() float64 {
|
||||||
|
return math.Max(k.GadgetryIncome*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TrueFishingIncome() float64 {
|
||||||
|
return math.Max(k.FishingIncome*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TrueConstructionIncome() float64 {
|
||||||
|
return math.Max(k.ConstructionIncome*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TruePropagandaExpense() float64 {
|
||||||
|
return math.Max(k.PropagandaExpense*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TrueBureaucracyExpense() float64 {
|
||||||
|
return math.Max(k.BureaucracyExpense*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TrueWarExpense() float64 {
|
||||||
|
return math.Max(k.WarExpense*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TrueQoLExpense() float64 {
|
||||||
|
return math.Max(k.QoLExpense*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TrueLogisticsExpense() float64 {
|
||||||
|
return math.Max(k.LogisticsExpense*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TrueDragonSubsExpense() float64 {
|
||||||
|
return math.Max(k.DragonSubsExpense*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TrueResearchSubsExpense() float64 {
|
||||||
|
return math.Max(k.ResearchSubsExpense*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TrueEducationExpense() float64 {
|
||||||
|
return math.Max(k.EducationExpense*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TrueHealthcareExpense() float64 {
|
||||||
|
return math.Max(k.HealthcareExpense*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TrueForeignRelExpense() float64 {
|
||||||
|
return math.Max(k.ForeignRelExpense*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TruePoliceExpense() float64 {
|
||||||
|
return math.Max(k.PoliceExpense*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TrueEconPlanExpense() float64 {
|
||||||
|
return math.Max(k.EconPlanExpense*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TrueParksExpense() float64 {
|
||||||
|
return math.Max(k.ParksExpense*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TrueFaithExpense() float64 {
|
||||||
|
return math.Max(k.FaithExpense*k.ProductivityMultiplier(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
func (k *KoboldMine) TotalSectorIncome() float64 {
|
func (k *KoboldMine) TotalSectorIncome() float64 {
|
||||||
return float64(k.Kobolds.Value) * (k.SectorMiningIncome.Value + k.SectorScavengingIncome.Value)
|
return float64(k.Kobolds()) * (math.Max(k.TrueMiningIncome(), 0.01) + math.Max(k.TrueScavengingIncome(), 0) + math.Max(k.TrueAlchemyIncome(), 0) + math.Max(k.TrueHospitalityIncome(), 0) + math.Max(k.TrueAgricultureIncome(), 0) + math.Max(k.TrueManufacturingIncome(), 0) + math.Max(k.TruePlanarIncome(), 0) + math.Max(k.TruePublishingIncome(), 0) + math.Max(k.TrueFinanceIncome(), 0) + math.Max(k.TrueGadgetryIncome(), 0) + math.Max(k.TrueFishingIncome(), 0) + math.Max(k.TrueConstructionIncome(), 0.02))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *KoboldMine) TotalGovExpense() float64 {
|
func (k *KoboldMine) TotalGovExpense() float64 {
|
||||||
return float64(k.Kobolds.Value) * (k.GovBureaucracyExpense.Value + k.GovWarExpense.Value)
|
return float64(k.Kobolds()) * (math.Max(k.TruePropagandaExpense(), 0) + math.Max(k.TrueBureaucracyExpense(), 0) + math.Max(k.TrueWarExpense(), 0) + math.Max(k.TrueQoLExpense(), 0) + math.Max(k.TrueLogisticsExpense(), 0) + math.Max(k.TrueDragonSubsExpense(), 0) + math.Max(k.TrueResearchSubsExpense(), 0) + math.Max(k.TrueEducationExpense(), 0) + math.Max(k.TrueHealthcareExpense(), 0) + math.Max(k.TrueForeignRelExpense(), 0) + math.Max(k.TruePoliceExpense(), 0) + math.Max(k.TrueEconPlanExpense(), 0) + math.Max(k.TrueParksExpense(), 0) + math.Max(k.TrueFaithExpense(), 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) Taxation() float64 {
|
||||||
|
return (k.TotalGovExpense() / (k.TotalSectorIncome() + k.TotalGovExpense())) * 100
|
||||||
|
}
|
||||||
|
|
||||||
|
// Idea for the tax rate. I could have a tracked "tax evasion" stat that drives up taxation (people who actually pay have to pay more) as well as a tracked "public pay ratio" stat that drives up taxation if the public sector is paid more than the private sector and vice versa if the public sector is paid less. Both stats should be exposed to the player.
|
||||||
|
// Idea for tax evasion. The corruption stat should increase tax evasion, while the squalor stat should reduce it. The link with corruption should be obvious, but for squalor: when the economy improves, more people have resources with which to attempt to protect their income.
|
||||||
|
|
||||||
|
func (k *KoboldMine) StatTaxRate() float64 {
|
||||||
|
return k.Taxation()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) Kobolds() int64 {
|
||||||
|
return int64((k.BasePopulation + (k.HealthcareExpense * 1000)) * k.FoodSupply * (1 - 0.5*(k.StatObesity()/100)) * (1 + k.TrueForeignRelations()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) DisplayedFoodSupply() float64 {
|
||||||
|
return (k.FoodSupply - 1) * 100
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) StatObesogenicity() float64 {
|
||||||
|
return (k.ObesogenicFood - 1) * 100
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) StatObesity() float64 {
|
||||||
|
return (100 / (2.3 + math.Exp(-0.04*(k.DisplayedFoodSupply()-37)))) + 100/(2.3+math.Exp(-0.04*(k.StatObesogenicity()-37)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) TrueForeignRelations() float64 {
|
||||||
|
return (1 - math.Max(math.Min(k.Secrecy, 1), 0)) * (math.Max(math.Min(k.ForeignRelations, 1), -1) - k.HiddenRelPenalty)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) DisplayedForeignRelations() float64 {
|
||||||
|
return math.Max(math.Min((k.ForeignRelations*100), 100), -100)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) DisplayedSecrecy() float64 {
|
||||||
|
return k.Secrecy * 100
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) SqualorReduction() float64 {
|
||||||
|
total := math.Max(k.MiningIncome, 0.01)
|
||||||
|
total -= math.Max(k.ScavengingIncome, 0)
|
||||||
|
total += math.Max(k.AlchemyIncome, 0)
|
||||||
|
total += math.Max(k.HospitalityIncome, 0) * 2
|
||||||
|
total += math.Max(k.ManufacturingIncome, 0)
|
||||||
|
total += math.Max(k.PlanarIncome, 0)
|
||||||
|
total += math.Max(k.PublishingIncome, 0) * 2
|
||||||
|
total += math.Max(k.FinanceIncome, 0) * 3
|
||||||
|
total += math.Max(k.GadgetryIncome, 0) * 2
|
||||||
|
total += math.Max(k.ConstructionIncome, 0.02) * 2
|
||||||
|
total += math.Max(k.PropagandaExpense, 0)
|
||||||
|
total += math.Max(k.BureaucracyExpense, 0) * 2
|
||||||
|
total += math.Max(k.QoLExpense, 0) * 20
|
||||||
|
total += math.Max(k.LogisticsExpense, 0)
|
||||||
|
total += math.Max(k.ResearchSubsExpense, 0) * 2
|
||||||
|
total += math.Max(k.EducationExpense, 0) * 4
|
||||||
|
total += math.Max(k.HealthcareExpense, 0) * 6
|
||||||
|
total += math.Max(k.ForeignRelExpense, 0)
|
||||||
|
total += math.Max(k.PoliceExpense, 0)
|
||||||
|
total += math.Max(k.EconPlanExpense, 0)
|
||||||
|
total += math.Max(k.ParksExpense, 0) * 3
|
||||||
|
total += math.Max(k.FaithExpense, 0) * 10
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KoboldMine) StatSqualor() float64 {
|
||||||
|
return 100 * math.Pow(1.204, 1.2-(2*k.SqualorReduction()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// So I want squalor to be a value between 100 and 0 that reduces readily at the start and then becomes increasingly difficult to further reduce. This initial squalor stat starts at about 80%, but may not change rapidly enough. Note that the 1.2-(2* sequence is meant to balance it at 80% starting point. If I change the velocity on the 2* side, I have to change the 1.2 offset as well. I really hope this math works, because I don't quite understand it.
|
||||||
|
|
||||||
|
func (k *KoboldMine) StatChaos() float64 {
|
||||||
|
return Mean(k.Rebellion, k.Madness, k.Cruelty)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the "crimes of chaos" stat for my crime calculations. It will also be displayed to the player as just Chaos. The player can see Cruelty, but not Madness or Rebellion. I'm concerned some players may think, "Oh, chaos is a good thing, I want more of that as long as it's not cruel!" In that case, they'll have mad, rebellious colonies that commit crimes against those perceived to be cruel...
|
||||||
|
|
||||||
|
func (k *KoboldMine) StatCorruption() float64 {
|
||||||
|
return Mean(k.Greed, k.Gullibility, k.Authoritarianism)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the "crimes of cunning" stat for my crime calculations. It will also be displayed to the player as just Corruption. The player can also see Greed and Authoritarianism, so they should have a good idea what goes into Corruption, but they'll have to infer the importance of Gullibility.
|
||||||
|
|
||||||
func (k *KoboldMine) Stats() []cardsim.Stat {
|
func (k *KoboldMine) Stats() []cardsim.Stat {
|
||||||
stats := cardsim.ExtractStats(k)
|
stats := cardsim.ExtractStats(k)
|
||||||
funcs := []cardsim.Stat{
|
funcs := []cardsim.Stat{
|
||||||
cardsim.StatFunc(
|
cardsim.StatFunc(
|
||||||
"Total Sector Mining Income",
|
"Mining Income",
|
||||||
k.ProductivityFunc(&k.SectorMiningIncome),
|
k.ProductivityFunc(k.TrueMiningIncome()),
|
||||||
),
|
),
|
||||||
cardsim.StatFunc(
|
cardsim.StatFunc(
|
||||||
"Total Sector Scavenging Income",
|
"Scavenging Income",
|
||||||
k.ProductivityFunc(&k.SectorScavengingIncome),
|
k.ProductivityFunc(k.TrueScavengingIncome()),
|
||||||
),
|
),
|
||||||
cardsim.StatFunc(
|
cardsim.StatFunc(
|
||||||
"Total Government Bureaucracy Expense",
|
"Alchemy Income",
|
||||||
k.ProductivityFunc(&k.GovBureaucracyExpense),
|
k.ProductivityFunc(k.TrueAlchemyIncome()),
|
||||||
),
|
),
|
||||||
cardsim.StatFunc(
|
cardsim.StatFunc(
|
||||||
"Total Government War Expense",
|
"Hospitality Income",
|
||||||
k.ProductivityFunc(&k.GovWarExpense),
|
k.ProductivityFunc(k.TrueHospitalityIncome()),
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Agriculture Income",
|
||||||
|
k.ProductivityFunc(k.TrueAgricultureIncome()),
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Manufacturing Income",
|
||||||
|
k.ProductivityFunc(k.TrueManufacturingIncome()),
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Planar Harvesting Income",
|
||||||
|
k.ProductivityFunc(k.TruePlanarIncome()),
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Book Publishing Income",
|
||||||
|
k.ProductivityFunc(k.TruePublishingIncome()),
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Forestry Income",
|
||||||
|
k.ProductivityFunc(k.TrueForestryIncome()),
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Finance Income",
|
||||||
|
k.ProductivityFunc(k.TrueFinanceIncome()),
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Gadgetry Income",
|
||||||
|
k.ProductivityFunc(k.TrueGadgetryIncome()),
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Fishing Income",
|
||||||
|
k.ProductivityFunc(k.TrueFishingIncome()),
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Construction Income",
|
||||||
|
k.ProductivityFunc(k.TrueConstructionIncome()),
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Propaganda Expense",
|
||||||
|
k.ProductivityFunc(k.TruePropagandaExpense()),
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Bureaucracy Expense",
|
||||||
|
k.ProductivityFunc(k.TrueBureaucracyExpense()),
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"War Expense",
|
||||||
|
k.ProductivityFunc(k.TrueWarExpense()),
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"QoL Expense",
|
||||||
|
k.ProductivityFunc(k.TrueQoLExpense()),
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Logistics Expense",
|
||||||
|
k.ProductivityFunc(k.TrueLogisticsExpense()),
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Dragon Subsidies",
|
||||||
|
k.ProductivityFunc(k.TrueDragonSubsExpense()),
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Research Subsidies",
|
||||||
|
k.ProductivityFunc(k.TrueResearchSubsExpense()),
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Education Expense",
|
||||||
|
k.ProductivityFunc(k.TrueEducationExpense()),
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Healthcare Expense",
|
||||||
|
k.ProductivityFunc(k.TrueHealthcareExpense()),
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Foreign Relations Expense",
|
||||||
|
k.ProductivityFunc(k.TrueForeignRelExpense()),
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Law Enforcement Expense",
|
||||||
|
k.ProductivityFunc(k.TruePoliceExpense()),
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Economic Planning Expense",
|
||||||
|
k.ProductivityFunc(k.TrueEconPlanExpense()),
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Parks and Aesthetics Expense",
|
||||||
|
k.ProductivityFunc(k.TrueParksExpense()),
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Faith Expense",
|
||||||
|
k.ProductivityFunc(k.TrueFaithExpense()),
|
||||||
),
|
),
|
||||||
cardsim.StatFunc(
|
cardsim.StatFunc(
|
||||||
"Total Sector Income",
|
"Total Sector Income",
|
||||||
@ -56,33 +414,78 @@ func (k *KoboldMine) Stats() []cardsim.Stat {
|
|||||||
"Total Government Expense",
|
"Total Government Expense",
|
||||||
k.TotalGovExpense,
|
k.TotalGovExpense,
|
||||||
),
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Foreign Relations",
|
||||||
|
k.DisplayedForeignRelations,
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Secrecy",
|
||||||
|
k.DisplayedSecrecy,
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Food Supply",
|
||||||
|
k.DisplayedFoodSupply,
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Kobolds",
|
||||||
|
k.Kobolds,
|
||||||
|
),
|
||||||
|
cardsim.InvisibleStatFunc(
|
||||||
|
"Squalor Reduction",
|
||||||
|
k.SqualorReduction,
|
||||||
|
),
|
||||||
|
cardsim.StatFunc(
|
||||||
|
"Squalor",
|
||||||
|
k.StatSqualor,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
stats = append(stats, funcs...)
|
stats = append(stats, funcs...)
|
||||||
cardsim.SortStats(stats)
|
// cardsim.SortStats(stats)
|
||||||
return stats
|
return stats
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewKoboldMine() *KoboldMine {
|
func NewKoboldMine() *KoboldMine {
|
||||||
return &KoboldMine{
|
return &KoboldMine{
|
||||||
Kobolds: cardsim.Stored[int64]{
|
BasePopulation: 1025,
|
||||||
Name: "Kobolds",
|
MiningIncome: 0.15,
|
||||||
Value: 1000,
|
ScavengingIncome: 0.1,
|
||||||
},
|
AlchemyIncome: 0.01,
|
||||||
SectorMiningIncome: cardsim.Stored[float64]{
|
HospitalityIncome: 0.0,
|
||||||
Name: "Sector Mining Income",
|
AgricultureIncome: 0.0,
|
||||||
Value: 0.15,
|
ManufacturingIncome: 0.10,
|
||||||
},
|
PlanarIncome: 0.00,
|
||||||
SectorScavengingIncome: cardsim.Stored[float64]{
|
PublishingIncome: 0.02,
|
||||||
Name: "Sector Scavenging Income",
|
ForestryIncome: 0.0,
|
||||||
Value: 0.1,
|
FinanceIncome: 0.02,
|
||||||
},
|
GadgetryIncome: 0.03,
|
||||||
GovBureaucracyExpense: cardsim.Stored[float64]{
|
FishingIncome: 0.0,
|
||||||
Name: "Government Bureaucracy Expense",
|
ConstructionIncome: 0.05,
|
||||||
Value: 0.05,
|
PropagandaExpense: 0.01,
|
||||||
},
|
BureaucracyExpense: 0.05,
|
||||||
GovWarExpense: cardsim.Stored[float64]{
|
WarExpense: 0.1,
|
||||||
Name: "Government War Expense",
|
QoLExpense: 0.01,
|
||||||
Value: 0.1,
|
LogisticsExpense: 0.02,
|
||||||
},
|
DragonSubsExpense: 0.0,
|
||||||
|
ResearchSubsExpense: 0.0,
|
||||||
|
EducationExpense: 0.01,
|
||||||
|
HealthcareExpense: 0.01,
|
||||||
|
ForeignRelExpense: 0.0,
|
||||||
|
PoliceExpense: 0.03,
|
||||||
|
EconPlanExpense: 0.02,
|
||||||
|
ParksExpense: 0.0,
|
||||||
|
FaithExpense: 0.03,
|
||||||
|
FoodSupply: 0.20,
|
||||||
|
ForeignRelations: -0.40,
|
||||||
|
HiddenRelPenalty: -0.01,
|
||||||
|
Rebellion: 0.10,
|
||||||
|
Madness: 0.25,
|
||||||
|
Cruelty: 0.45,
|
||||||
|
Greed: 0.35,
|
||||||
|
Gullibility: 0.10,
|
||||||
|
Authoritarianism: 0.70,
|
||||||
|
Secrecy: .95,
|
||||||
|
PointOfDimReturns: 1.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note that the mine initializes with 1.02 points of overall productivity.
|
||||||
|
22
koboldsim/util.go
Normal file
22
koboldsim/util.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package koboldsim
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
// Generic helper functions not directly attached to Card Sim Engine mechanics.
|
||||||
|
|
||||||
|
// Mean calculates the mathematical mean of its arguments (the sum, divided
|
||||||
|
// by the number of elements). If it is called with no arguments, it returns
|
||||||
|
// NaN ("not a number"). If there are contradictory infinities among the
|
||||||
|
// arguments, it also returns NaN. Overflowing or underflowing can create an
|
||||||
|
// infinity.
|
||||||
|
func Mean(vals ...float64) float64 {
|
||||||
|
if len(vals) == 0 {
|
||||||
|
return math.NaN()
|
||||||
|
}
|
||||||
|
|
||||||
|
total := 0.0
|
||||||
|
for _, x := range vals {
|
||||||
|
total += x
|
||||||
|
}
|
||||||
|
return total / float64(len(vals))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user