Prototype for TablePolicy
This commit is contained in:
		
							
								
								
									
										27
									
								
								.vscode/koboldsimsnippets.code-snippets
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								.vscode/koboldsimsnippets.code-snippets
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| { | ||||
| 	// Place your KoboldSim workspace snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and  | ||||
| 	// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope  | ||||
| 	// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is  | ||||
| 	// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:  | ||||
| 	// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.  | ||||
| 	// Placeholders with the same ids are connected. | ||||
| 	// Example: | ||||
| 	// "Print to console": { | ||||
| 	// 	"scope": "javascript,typescript", | ||||
| 	// 	"prefix": "log", | ||||
| 	// 	"body": [ | ||||
| 	// 		"console.log('$1');", | ||||
| 	// 		"$2" | ||||
| 	// 	], | ||||
| 	// 	"description": "Log output to console" | ||||
| 	// } | ||||
| 	"KoboldMine Add case": { | ||||
| 		"scope": "go", | ||||
| 		"prefix": "case", | ||||
| 		"body": [ | ||||
| 			"case ${1:field}:", | ||||
| 			"\tk.${1:field} += amount", | ||||
| 			"\treturn k.${1:field}", | ||||
| 		], | ||||
| 	} | ||||
| } | ||||
| @@ -9,6 +9,7 @@ import ( | ||||
| var ( | ||||
| 	ErrOptionNotEnabled = errors.New("option not enabled") | ||||
| 	ErrPolicyNotEnacted = errors.New("cannot unenact policy that is not enacted") | ||||
| 	ErrNoFieldLabel = errors.New("field does not exist") | ||||
|  | ||||
| 	// ErrUnimplemented and ErrKeepMessaage are "non-errors". They are used | ||||
| 	// as special signals that the result needs to be handled in a special way; | ||||
| @@ -190,7 +191,7 @@ type BasicPolicy struct { | ||||
| } | ||||
|  | ||||
| // YesWeCan returns true. It's the default value for BasicPolicy.CanDo / BasicPolicy.CanUndo. | ||||
| func YesWeCan(*BasicPolicy, *Player) bool { | ||||
| func YesWeCan[T any](T, *Player) bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| @@ -260,6 +261,94 @@ func (b *BasicPolicy) Is(p Policy) bool { | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // TablePolicy is a Policy where all numerical changes are defined by | ||||
| // adding a constant to some set of fields (defined by `EffectsTable``) | ||||
| // and subtracting it back out when de-enacting. If the currently | ||||
| // enacted option is re-enacted, it refunds the player's action point. | ||||
| type TablePolicy struct { | ||||
| 	Desc           cardsim.Message | ||||
| 	UnenactedDesc  cardsim.Message | ||||
| 	EnactedDesc    cardsim.Message | ||||
| 	NothingChanged cardsim.message | ||||
| 	EffectsTable map[FieldLabel]float64 | ||||
| 	CanDo          func(*TablePolicy, *Player) bool | ||||
|  | ||||
| 	CurrentlyEnacted  bool | ||||
| 	LastEnactedPolicy Policy | ||||
| 	LastEnactedIdx    int | ||||
| } | ||||
|  | ||||
| // LastEnacted notifies t about the last-enacted policy in its group. It updates | ||||
| // t.currentlyEnacted accordingly. | ||||
| func (t *TablePolicy) LastEnacted(i int, p Policy) { | ||||
| 	t.LastEnactedPolicy = p | ||||
| 	t.LastEnactedIdx = i | ||||
| 	t.CurrentlyEnacted = t.Is(p) | ||||
| } | ||||
|  | ||||
| // OptionText implements CardOption. | ||||
| func (t *TablePolicy) OptionText(*Player) (cardsim.Message, error) { | ||||
| 	if t.CurrentlyEnacted { | ||||
| 		if t.EnactedDesc == nil { | ||||
| 			return nil, ErrUnimplemented | ||||
| 		} | ||||
| 		return t.EnactedDesc, nil | ||||
| 	} | ||||
| 	if t.UnenactedDesc == nil { | ||||
| 		return nil, ErrUnimplemented | ||||
| 	} | ||||
| 	return t.UnenactedDesc, nil | ||||
| } | ||||
|  | ||||
| // Enact implements CardOption. | ||||
| func (t *TablePolicy) Enact(p *Player) (cardsim.Message, error) { | ||||
| 	if t.EffectsTable == nil { | ||||
| 		return nil, ErrUnimplemented | ||||
| 	} | ||||
| 	if t.CurrentlyEnacted { | ||||
| 		p.ActionsRemaining++ | ||||
| 		if t.NothingChanged == nil { | ||||
| 			t.NothingChanged = cardsim.MsgStr("You continue your current approach.") | ||||
| 		} | ||||
| 		return t.NothingChanged, nil | ||||
| 	} | ||||
| 	var errs cardsim.ErrorCollector | ||||
| 	for label, amount := range t.EffectsTable { | ||||
| 		errs.Add(p.Stats.Add(label, amount)) | ||||
| 	} | ||||
| 	return t.EnactedDesc, errs.Emit() | ||||
| } | ||||
|  | ||||
| // Unenact implements Policy. | ||||
| func (t *TablePolicy) Unenact(p *Player) error { | ||||
| 	if !t.CurrentlyEnacted { | ||||
| 		return ErrPolicyNotEnacted | ||||
| 	} | ||||
| 	var errs cardsim.ErrorCollector | ||||
| 	for label, amount := range t.EffectsTable { | ||||
| 		errs.Add(p.Stats.Add(label, -amount) | ||||
| 	} | ||||
| 	return errs.Emit() | ||||
| } | ||||
|  | ||||
| // Enabled implements CardOption. | ||||
| func (t *TablePolicy) Enabled(p *Player) bool { | ||||
| 	if t.CurrentlyEnacted { | ||||
| 		return true | ||||
| 	} | ||||
| 	if t.CanDo == nil { | ||||
| 		panic(ErrUnimplemented) | ||||
| 	} | ||||
| 	return t.CanDo(t, p) | ||||
| } | ||||
|  | ||||
| func (t *TablePolicy) Is(p Policy) bool { | ||||
| 	if o, ok := p.(*TablePolicy); ok { | ||||
| 		return o == b | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // A VerbosePolicy is a group of related policies pretending to all be the same | ||||
| // policy. Which policy is used is determined by what the previous policy for | ||||
| // the card was (as reported via a call to LastEnacted): | ||||
|   | ||||
| @@ -164,3 +164,50 @@ func NewKoboldMine() *KoboldMine { | ||||
| 		Secrecy:           95, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type FieldLabel string | ||||
|  | ||||
| const ( | ||||
| 	BasePopulation FieldLabel = "BasePopulation" | ||||
| 	Scavenging FieldLabel = "Scavenging" | ||||
| 	Militarism FieldLabel = "Militarism" | ||||
| 	FoodSupply FieldLabel = "FoodSupply" | ||||
| 	ForeignRelations FieldLabel = "ForeignRelations" | ||||
| 	Rebellion FieldLabel = "Rebellion" | ||||
| 	Madness FieldLabel = "Madness" | ||||
| 	Cruelty FieldLabel = "Cruelty" | ||||
| 	Authoritarianism FieldLabel = "Authoritarianism" | ||||
| ) | ||||
|  | ||||
| func (k *KoboldMine) Add(which FieldLabel, amount float64) error { | ||||
| 	switch(which) { | ||||
| 	case BasePopulation: | ||||
| 		k.BasePopulation += amount | ||||
| 		return nil | ||||
| 	case Scavenging: | ||||
| 		k.Scavenging += amount | ||||
| 		return nil | ||||
| 	case Militarism: | ||||
| 		k.Militarism += amount | ||||
| 		return nil | ||||
| 	case FoodSupply: | ||||
| 		k.FoodSupply += amount | ||||
| 		return nil | ||||
| 	case ForeignRelations: | ||||
| 		k.ForeignRelations += amount | ||||
| 		return nil | ||||
| 	case Rebellion: | ||||
| 		k.Rebellion += amount | ||||
| 		return nil | ||||
| 	case Madness: | ||||
| 		k.Madness += amount | ||||
| 		return nil | ||||
| 	case Cruelty: | ||||
| 		k.Cruelty += amount | ||||
| 		return nil | ||||
| 	case Authoritarianism: | ||||
| 		k.Authoritarianism += amount | ||||
| 		return nil | ||||
| 	} | ||||
| 	return fmt.Errorf("cannot add %d to %q: %w", amount, which, ErrNoFieldLabel) | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user