mirror of
https://github.com/BigJk/end_of_eden.git
synced 2026-02-06 10:48:09 +00:00
Added fuzzy tester.
This commit is contained in:
parent
0f05b9e052
commit
3eec5bfc7d
@ -14,7 +14,7 @@ register_status_effect("WEAKEN", {
|
||||
decay = DECAY_ALL,
|
||||
rounds = 1,
|
||||
callbacks = {
|
||||
on_damage_calc = function()
|
||||
on_damage_calc = function(ctx)
|
||||
if ctx.source == ctx.owner then
|
||||
return ctx.damage - ctx.stacks * 2
|
||||
end
|
||||
|
||||
@ -103,7 +103,7 @@ register_story_teller("STAGE_2", {
|
||||
-- BOSS
|
||||
end
|
||||
|
||||
return nil
|
||||
return GAME_STATE_FIGHT
|
||||
end
|
||||
})
|
||||
|
||||
@ -125,6 +125,6 @@ register_story_teller("STAGE_3", {
|
||||
-- BOSS
|
||||
end
|
||||
|
||||
return nil
|
||||
return GAME_STATE_FIGHT
|
||||
end
|
||||
})
|
||||
92
cmd/fuzzy-tester/main.go
Normal file
92
cmd/fuzzy-tester/main.go
Normal file
@ -0,0 +1,92 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/BigJk/end_of_eden/game"
|
||||
"github.com/samber/lo"
|
||||
"math/rand"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var endTime int64
|
||||
var seed int64
|
||||
var mods []string
|
||||
|
||||
func main() {
|
||||
fmt.Println("End Of Eden :: Fuzzy Tester")
|
||||
fmt.Println("The fuzzy tester hits a game session with a random number of operations and tries to trigger a panic.")
|
||||
fmt.Println()
|
||||
|
||||
routines := flag.Int("n", 0, "number of goroutines")
|
||||
timeout := flag.Duration("timeout", time.Minute, "length of testing")
|
||||
baseSeed := flag.Int64("seed", 0, "random seed")
|
||||
modsString := flag.String("mods", "", "mods to load and test, separated by ',' (e.g. mod1,mod2,mod3)")
|
||||
flag.Parse()
|
||||
|
||||
if *routines == 0 {
|
||||
flag.PrintDefaults()
|
||||
return
|
||||
}
|
||||
|
||||
seed = *baseSeed
|
||||
endTime = time.Now().Add(*timeout).Unix()
|
||||
|
||||
if len(*modsString) > 0 {
|
||||
mods = strings.Split(*modsString, ",")
|
||||
}
|
||||
|
||||
if *baseSeed == 0 {
|
||||
seed = rand.Int63()
|
||||
}
|
||||
|
||||
fmt.Println("N :", *routines)
|
||||
fmt.Println("Seed :", seed)
|
||||
fmt.Println("\nWorking...")
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
for i := 0; i < *routines; i++ {
|
||||
wg.Add(1)
|
||||
tester(i, wg)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func tester(index int, wg *sync.WaitGroup) {
|
||||
rnd := rand.New(rand.NewSource(seed + int64(index)))
|
||||
opKeys := lo.Keys(Operations)
|
||||
stack := [][]string{}
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Println(stack)
|
||||
fmt.Println(r)
|
||||
fmt.Println(string(debug.Stack()))
|
||||
os.Exit(-1)
|
||||
}
|
||||
}()
|
||||
|
||||
for time.Now().Unix() < endTime {
|
||||
s := game.NewSession(game.WithMods(mods))
|
||||
ops := 5 + rand.Intn(100)
|
||||
stack = [][]string{}
|
||||
s.SetOnLuaError(func(file string, line int, callback string, typeId string, err error) {
|
||||
fmt.Println("File :", file)
|
||||
fmt.Println("Line :", line)
|
||||
fmt.Println("Callback :", callback)
|
||||
fmt.Println("TypeId :", typeId)
|
||||
fmt.Println("Err :", err)
|
||||
panic("lua error")
|
||||
})
|
||||
|
||||
for i := 0; i < ops; i++ {
|
||||
next := lo.Shuffle(opKeys)[0]
|
||||
stack = append(stack, []string{next, Operations[next](rnd, s)})
|
||||
}
|
||||
}
|
||||
}
|
||||
126
cmd/fuzzy-tester/operations.go
Normal file
126
cmd/fuzzy-tester/operations.go
Normal file
@ -0,0 +1,126 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/BigJk/end_of_eden/game"
|
||||
"github.com/samber/lo"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
func Shuffle[T any](rnd *rand.Rand, collection []T) []T {
|
||||
rnd.Shuffle(len(collection), func(i, j int) {
|
||||
collection[i], collection[j] = collection[j], collection[i]
|
||||
})
|
||||
|
||||
return collection
|
||||
}
|
||||
|
||||
var Operations = map[string]func(rnd *rand.Rand, s *game.Session) string{
|
||||
"FinishPlayerTurn": func(rnd *rand.Rand, s *game.Session) string {
|
||||
s.FinishPlayerTurn()
|
||||
return "Finish player turn"
|
||||
},
|
||||
"FinishFight": func(rnd *rand.Rand, s *game.Session) string {
|
||||
s.FinishFight()
|
||||
return "Finish fight"
|
||||
},
|
||||
"CastCard": func(rnd *rand.Rand, s *game.Session) string {
|
||||
guid := Shuffle(rnd, lo.Flatten([][]string{{""}, s.GetInstances(), s.GetActors()}))[0]
|
||||
target := Shuffle(rnd, lo.Flatten([][]string{{""}, s.GetInstances(), s.GetActors()}))[0]
|
||||
s.CastCard(guid, target)
|
||||
return fmt.Sprintf("Cast card with guid '%s' on '%s'", guid, target)
|
||||
},
|
||||
"AddActorFromEnemy": func(rnd *rand.Rand, s *game.Session) string {
|
||||
res := s.GetResources()
|
||||
enemyId := Shuffle(rnd, lo.Flatten([][]string{{""}, lo.Keys(res.Enemies)}))[0]
|
||||
s.AddActorFromEnemy(enemyId)
|
||||
return fmt.Sprintf("Added enemy '%s'", enemyId)
|
||||
},
|
||||
"SetEvent": func(rnd *rand.Rand, s *game.Session) string {
|
||||
res := s.GetResources()
|
||||
eventId := Shuffle(rnd, lo.Flatten([][]string{{""}, lo.Keys(res.Events)}))[0]
|
||||
s.SetEvent(eventId)
|
||||
return fmt.Sprintf("Set event '%s'", eventId)
|
||||
},
|
||||
"SetGameState": func(rnd *rand.Rand, s *game.Session) string {
|
||||
res := s.GetResources()
|
||||
eventId := Shuffle(rnd, lo.Flatten([][]string{{""}, lo.Keys(res.Events)}))[0]
|
||||
s.SetGameState(Shuffle(rnd, []game.GameState{game.GameStateGameOver, game.GameStateMerchant, game.GameStateRandom, game.GameStateEvent, game.GameStateFight, game.GameState("")})[0])
|
||||
return fmt.Sprintf("Set event '%s'", eventId)
|
||||
},
|
||||
"FinishEvent": func(rnd *rand.Rand, s *game.Session) string {
|
||||
res := s.GetResources()
|
||||
eventId := Shuffle(rnd, lo.Flatten([][]string{lo.Keys(res.Events)}))[0]
|
||||
event := res.Events[eventId]
|
||||
choice := rnd.Intn(len(event.Choices) + 1)
|
||||
s.FinishEvent(choice)
|
||||
return fmt.Sprintf("Finish event '%s' with choice %d", eventId, choice)
|
||||
},
|
||||
"CleanUpFight": func(rnd *rand.Rand, s *game.Session) string {
|
||||
s.CleanUpFight()
|
||||
return "Clean up fight"
|
||||
},
|
||||
"SetupFight": func(rnd *rand.Rand, s *game.Session) string {
|
||||
s.SetupFight()
|
||||
return "Setup fight"
|
||||
},
|
||||
"SetupMerchant": func(rnd *rand.Rand, s *game.Session) string {
|
||||
s.SetupMerchant()
|
||||
return "Setup merchant"
|
||||
},
|
||||
"LeaveMerchant": func(rnd *rand.Rand, s *game.Session) string {
|
||||
s.LeaveMerchant()
|
||||
return "Leave merchant"
|
||||
},
|
||||
"GivePlayerGold": func(rnd *rand.Rand, s *game.Session) string {
|
||||
gold := rnd.Intn(100)
|
||||
s.GivePlayerGold(gold)
|
||||
return fmt.Sprintf("Give %d gold to player", gold)
|
||||
},
|
||||
"PlayerGiveActionPoints": func(rnd *rand.Rand, s *game.Session) string {
|
||||
actionPoints := rnd.Intn(5)
|
||||
s.PlayerGiveActionPoints(actionPoints)
|
||||
return fmt.Sprintf("Give %d action points to player", actionPoints)
|
||||
},
|
||||
"AddCard": func(rnd *rand.Rand, s *game.Session) string {
|
||||
res := s.GetResources()
|
||||
cardId := Shuffle(rnd, lo.Flatten([][]string{{""}, lo.Keys(res.Cards)}))[0]
|
||||
s.GiveCard(cardId, game.PlayerActorID)
|
||||
return fmt.Sprintf("Give '%s' card to player", cardId)
|
||||
},
|
||||
"AddArtifact": func(rnd *rand.Rand, s *game.Session) string {
|
||||
res := s.GetResources()
|
||||
artifactId := Shuffle(rnd, lo.Flatten([][]string{{""}, lo.Keys(res.Artifacts)}))[0]
|
||||
s.GiveArtifact(artifactId, game.PlayerActorID)
|
||||
return fmt.Sprintf("Give '%s' artifact to player", artifactId)
|
||||
},
|
||||
"PlayerBuyCard": func(rnd *rand.Rand, s *game.Session) string {
|
||||
res := s.GetResources()
|
||||
cardId := Shuffle(rnd, lo.Flatten([][]string{{""}, lo.Keys(res.Cards)}))[0]
|
||||
s.PlayerBuyCard(cardId)
|
||||
return fmt.Sprintf("Buy '%s' card as player", cardId)
|
||||
},
|
||||
"PlayerBuyArtifact": func(rnd *rand.Rand, s *game.Session) string {
|
||||
res := s.GetResources()
|
||||
artifactId := Shuffle(rnd, lo.Flatten([][]string{{""}, lo.Keys(res.Artifacts)}))[0]
|
||||
s.PlayerBuyArtifact(artifactId)
|
||||
return fmt.Sprintf("Buy '%s' artifact as player", artifactId)
|
||||
},
|
||||
"AddStatusEffect": func(rnd *rand.Rand, s *game.Session) string {
|
||||
res := s.GetResources()
|
||||
effectId := Shuffle(rnd, lo.Flatten([][]string{{""}, lo.Keys(res.StatusEffects)}))[0]
|
||||
stacks := rnd.Intn(10)
|
||||
s.GiveStatusEffect(effectId, game.PlayerActorID, stacks)
|
||||
return fmt.Sprintf("Give '%s' status effect with %d stacks to player", effectId, stacks)
|
||||
},
|
||||
"BuyUpgradeCard": func(rnd *rand.Rand, s *game.Session) string {
|
||||
cardId := Shuffle(rnd, lo.Flatten([][]string{{""}, s.GetInstances()}))[0]
|
||||
s.BuyUpgradeCard(cardId)
|
||||
return fmt.Sprintf("Buy upgrading card '%s'", cardId)
|
||||
},
|
||||
"BuyRemoveCard": func(rnd *rand.Rand, s *game.Session) string {
|
||||
cardId := Shuffle(rnd, lo.Flatten([][]string{{""}, s.GetInstances()}))[0]
|
||||
s.BuyRemoveCard(cardId)
|
||||
return fmt.Sprintf("Buy removing card '%s'", cardId)
|
||||
},
|
||||
}
|
||||
@ -35,7 +35,7 @@ register_artifact(
|
||||
|
||||
func TestArtifact(t *testing.T) {
|
||||
s := lua.NewState()
|
||||
man := NewResourcesManager(s, log.New(io.Discard, "", 0))
|
||||
man := NewResourcesManager(s, nil, log.New(io.Discard, "", 0))
|
||||
|
||||
// Evaluate lua
|
||||
if !assert.NoError(t, s.DoString(TestArtifactLua)) {
|
||||
|
||||
@ -201,6 +201,10 @@ func (man *ResourcesManager) luaDeleteEvent(l *lua.LState) int {
|
||||
}
|
||||
|
||||
func (man *ResourcesManager) defineDocs(docs *ludoc.Docs) {
|
||||
if docs == nil {
|
||||
return
|
||||
}
|
||||
|
||||
docs.Category("Content Registry", "These functions are used to define new content in the base game and in mods.", 100)
|
||||
|
||||
docs.Function("register_artifact", fmt.Sprintf("Registers a new artifact.\n\n```lua\n%s\n```", `register_artifact("REPULSION_STONE",
|
||||
|
||||
@ -264,6 +264,10 @@ func (s *Session) GobDecode(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Session) GetResources() *ResourcesManager {
|
||||
return s.resources
|
||||
}
|
||||
|
||||
//
|
||||
// Internal
|
||||
//
|
||||
@ -574,7 +578,7 @@ func (s *Session) FinishFight() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// FinishEvent finishes a event with the given choice. If the game state is not in the EVENT state this
|
||||
// FinishEvent finishes an event with the given choice. If the game state is not in the EVENT state this
|
||||
// does nothing.
|
||||
func (s *Session) FinishEvent(choice int) {
|
||||
if len(s.currentEvent) == 0 || s.state != GameStateEvent {
|
||||
@ -849,7 +853,7 @@ func (s *Session) GetInstances() []string {
|
||||
return lo.Keys(s.instances)
|
||||
}
|
||||
|
||||
// GetInstance returns a instance by guid. An instance is a CardInstance or ArtifactInstance.
|
||||
// GetInstance returns an instance by guid. An instance is a CardInstance or ArtifactInstance.
|
||||
func (s *Session) GetInstance(guid string) any {
|
||||
return s.instances[guid]
|
||||
}
|
||||
@ -983,6 +987,13 @@ func (s *Session) GiveStatusEffect(typeId string, owner string, stacks int) stri
|
||||
}
|
||||
|
||||
status := s.resources.StatusEffects[typeId]
|
||||
if status == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if _, ok := s.actors[owner]; !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
// TODO: This should always be either 0 or 1 len, so the logic down below is a bit meh.
|
||||
same := lo.Filter(s.actors[owner].StatusEffects.ToSlice(), func(guid string, index int) bool {
|
||||
@ -995,7 +1006,7 @@ func (s *Session) GiveStatusEffect(typeId string, owner string, stacks int) stri
|
||||
})
|
||||
|
||||
if len(same) > 1 {
|
||||
log.Println("Error: status effect duplicate!")
|
||||
panic("Error: status effect duplicate!")
|
||||
}
|
||||
|
||||
// If it can't stack we delete all existing instances
|
||||
@ -1035,7 +1046,11 @@ func (s *Session) GiveStatusEffect(typeId string, owner string, stacks int) stri
|
||||
|
||||
// RemoveStatusEffect removes a status effect by guid.
|
||||
func (s *Session) RemoveStatusEffect(guid string) {
|
||||
instance := s.instances[guid].(StatusEffectInstance)
|
||||
instance, ok := s.instances[guid].(StatusEffectInstance)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := s.resources.StatusEffects[instance.TypeID].Callbacks[CallbackOnStatusRemove].Call(CreateContext("type_id", instance.TypeID, "guid", guid, "owner", instance.Owner)); err != nil {
|
||||
s.logLuaError(CallbackOnStatusRemove, instance.TypeID, err)
|
||||
}
|
||||
@ -1056,7 +1071,11 @@ func (s *Session) GetActorStatusEffects(guid string) []string {
|
||||
|
||||
// AddStatusEffectStacks increases the stacks of a certain status effect by guid.
|
||||
func (s *Session) AddStatusEffectStacks(guid string, stacks int) {
|
||||
instance := s.instances[guid].(StatusEffectInstance)
|
||||
instance, ok := s.instances[guid].(StatusEffectInstance)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
instance.Stacks += stacks
|
||||
if instance.Stacks <= 0 {
|
||||
s.RemoveStatusEffect(guid)
|
||||
@ -1067,7 +1086,11 @@ func (s *Session) AddStatusEffectStacks(guid string, stacks int) {
|
||||
|
||||
// SetStatusEffectStacks sets the stacks of a certain status effect by guid.
|
||||
func (s *Session) SetStatusEffectStacks(guid string, stacks int) {
|
||||
instance := s.instances[guid].(StatusEffectInstance)
|
||||
instance, ok := s.instances[guid].(StatusEffectInstance)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
instance.Stacks = stacks
|
||||
if instance.Stacks <= 0 {
|
||||
s.RemoveStatusEffect(guid)
|
||||
@ -1136,6 +1159,10 @@ func (s *Session) GetArtifact(guid string) (*Artifact, ArtifactInstance) {
|
||||
|
||||
// GiveArtifact gives an artifact to an actor.
|
||||
func (s *Session) GiveArtifact(typeId string, owner string) string {
|
||||
if _, ok := s.resources.Artifacts[typeId]; !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
instance := ArtifactInstance{
|
||||
TypeID: typeId,
|
||||
GUID: NewGuid("ARTIFACT"),
|
||||
@ -1186,6 +1213,10 @@ func (s *Session) GetCard(guid string) (*Card, CardInstance) {
|
||||
}
|
||||
|
||||
func (s *Session) GiveCard(typeId string, owner string) string {
|
||||
if _, ok := s.resources.Cards[typeId]; !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
instance := CardInstance{
|
||||
TypeID: typeId,
|
||||
GUID: NewGuid("CARD"),
|
||||
@ -1353,6 +1384,10 @@ func (s *Session) UpgradeCard(guid string) bool {
|
||||
//
|
||||
|
||||
func (s *Session) DealDamage(source string, target string, damage int, flat bool) int {
|
||||
if _, ok := s.actors[source]; !ok {
|
||||
return 0
|
||||
}
|
||||
|
||||
val, ok := s.actors[target]
|
||||
if !ok {
|
||||
return 0
|
||||
@ -1499,7 +1534,10 @@ func (s *Session) GetActors() []string {
|
||||
}
|
||||
|
||||
func (s *Session) GetActor(id string) Actor {
|
||||
return s.actors[id]
|
||||
if val, ok := s.actors[id]; ok {
|
||||
return val
|
||||
}
|
||||
return NewActor("")
|
||||
}
|
||||
|
||||
func (s *Session) UpdateActor(id string, update func(actor *Actor) bool) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user