Basic engine for NationStates-like "make decisions on issues" simulation games. Very basic.
Go to file
2023-04-15 20:16:54 -07:00
.idea Initial commit. 2023-03-26 23:40:44 -07:00
cardsim Debug action to adjust debug level. 2023-04-15 20:16:54 -07:00
smoketest Don't stomp on the default debugers in SmokeTest. 2023-04-15 19:17:34 -07:00
.gitignore Fix module spec. 2023-04-02 22:25:12 -07:00
go.mod Major stats upgrade. 2023-04-04 11:12:07 -07:00
go.sum Major stats upgrade. 2023-04-04 11:12:07 -07:00
LICENSE Initial commit 2023-03-27 06:38:10 +00:00
README.md stats manual in README 2023-04-04 12:29:36 -07:00

CardSimEngine

Basic engine for NationStates-like "make decisions on issues" simulation games. Very incomplete.

Go Reference

General turn model

  1. Player has a hand of cards. Each card has one or more actions available.
  2. Player chooses one action on one card.
  3. Action runs.
  4. Card is removed.
  5. Every rule in the player's rule collection runs, in order from lowest "step" to highest. If multiple rules have the same step number, they are run in shuffled order.
  6. Any changes to the rule collection requested during rule evaluation (presumably, commanded by a rule) are applied after rules are applied.
  7. Turn counter increments.
  8. Player draws back to their hand limit. (If the deck and hand are empty, the game ends with an "end of history" error.)
  9. Game outputs all visible stats, deferred messages, warning messages, other game status updates, etc.
  10. Next turn.

Abstractions

Rule

An arbitrary function running on player data, with a name and a step it runs in. It can ask to remove itself after execution.

RuleCollection

Bucket of rules which may be updated during the game. Responsible for running rules (in some appropriate order) for the turn.

Card

An event the user can act on.

CardOption

A choice the user can make in response to some event and its consequences. Actions from CardOption instances are similar to rules, although changes to the actual rule collection are applied immediately.

Player

A bucket of game state.

Stat

An arbitrary variable (or function, if it's calculated) tagged with some stuff to make it easier to display to the player. Stats can be extracted automatically (see "Stat Extraction" below).

StatsCollection

An arbitrary struct that contains the player's stats for some set of rules. Can be thought of as "the game-specific part of the player".

Errors and warnings

Errors wrapped in the Warning type, including those created by Warningf, represent events that should be reported (with stack traces, in debug mode) but shoudl not crash the game.

There are some special errors that Rules can use to "communicate with" the rule execution logic in RulesCollection. Other non-Warning errors crash.

Messages

For now, strings but inconvenient. Intended to provide forwards compatibility when we eventually include some way to format text, where all the stuff written for "it's just a string" would break if not for having this extra type in the way to wrap it where we can stay compatible with "it's just a string" mode.

Stat Extraction

The function ExtractStats creates a stats list automatically by searching through a struct's fields and methods. The following things are recognized as stats:

  • any method with a name like StatFoo or HiddenStatFoo (the latter are marked as invisible stats, which show up only in debug mode with the implementation in BasicStatsPane)
  • any exported field with a type that is already a Stat; the Stored[T] and Hidden[T] generic types are containers for this
  • any exported field tagged with cardsim:"stat"
    • or cardsim:"hidden" for hidden stats. "hiddenstat" also works.
    • you can use "round2" to round to two decimal places -- you can use any integer here, not just 2. works with both float types.
    • "hiddenround3" (or any other number) creates a hidden rounded stat.
    • To change the display name of a stat, use a separate tag phrase in addition to the stat tag, cardsim_name:"name".
      • For example: cardsim:"stat" cardsim_name:"Stat Display Name" creates a visible stat that shows up as "Stat Display Name".
      • cardsim:"hiddenround1" cardsim_name:"Hidden Rounded Stat" creates an invisible stat that shows up, rounded to one decimal place, as "Hidden Rounded Stat".

Stat extraction can implement most or all of your type's Stats method for you:

func (e *ExampleType) Stats() []cardsim.Stat {
    return cardsim.ExtractStats(e)
}

ExtractStats puts methods first (lexicographically), then fields (in the order they appear). You can use cardsim.SortStats to instead put visible stats before hidden stats, alphabetized (case-insensitive).