66
77namespace Mythril . Headless . Simulation ;
88
9- public class RoutedSimulator (
9+ public partial class RoutedSimulator (
1010 Items items , Quests quests , QuestDetails questDetails , QuestUnlocks questUnlocks ,
1111 QuestToCadenceUnlocks questToCadenceUnlocks , Cadences cadences , Locations locations ,
1212 ItemRefinements refinements , StatAugments statAugments , Stats stats )
@@ -18,185 +18,23 @@ public void Run()
1818 {
1919 Console . WriteLine ( "Starting Path-Routed Simulation..." ) ;
2020 var state = new SimulationState ( stats ) ;
21- bool progressed = true ; int steps = 0 ; const int MAX_STEPS = 5000 ;
21+ bool progressed = true ; int steps = 0 ; const int MAX_STEPS = 10000 ;
2222 while ( progressed && steps < MAX_STEPS )
2323 {
2424 steps ++ ;
25- progressed = AttemptStep ( state ) ;
25+ progressed = AttemptStep ( state , steps ) ;
2626 if ( state . CompletedQuests . Contains ( END_QUEST ) ) { Console . WriteLine ( $ "[SUCCESS] End Game reached!") ; break ; }
2727 if ( state . CurrentTime > 3600 * 24 * 365 ) break ;
2828 }
2929 Console . WriteLine ( $ "Routed Completion Time: { ( state . CurrentTime / 60.0 ) : F1} minutes") ;
3030 Console . WriteLine ( $ "Total Quests Completed: { state . CompletedQuests . Count } ") ;
31- if ( ! state . CompletedQuests . Contains ( END_QUEST ) ) Console . WriteLine ( "[FAIL] End Game node never reached." ) ;
32- }
33-
34- private bool AttemptStep ( SimulationState state )
35- {
36- var available = GetAvailableQuests ( state ) ;
37- var targetable = available . Where ( q => ! state . CompletedQuests . Contains ( q . Quest . Name ) ) . OrderBy ( q => q . Detail . DurationSeconds ) . ToList ( ) ;
38-
39- foreach ( var target in targetable ) {
40- if ( CanAffordEventually ( state , target . Detail . Requirements ) ) {
41- ExecuteQuest ( state , target . Quest , target . Detail ) ; return true ;
42- }
43- }
44-
45- var uncompletedSingles = locations . All . SelectMany ( l => l . Quests ?? [ ] ) . Where ( q => ( questDetails [ q ] . Type != QuestType . Recurring ) && ! state . CompletedQuests . Contains ( q . Name ) ) . ToList ( ) ;
46- foreach ( var q in uncompletedSingles ) {
47- var prereqs = questUnlocks [ q ] ; if ( prereqs == null ) continue ;
48- foreach ( var pre in prereqs ) {
49- if ( ! state . CompletedQuests . Contains ( pre . Name ) ) {
50- var match = available . FirstOrDefault ( x => x . Quest . Name == pre . Name ) ;
51- if ( match . Quest . Name != null && CanAffordEventually ( state , match . Detail . Requirements ) ) {
52- ExecuteQuest ( state , match . Quest , match . Detail ) ; return true ;
53- }
54- }
55- }
56- }
57-
58- foreach ( var cadName in state . UnlockedCadences ) {
59- var cadence = cadences . All . FirstOrDefault ( c => c . Name == cadName ) ;
60- if ( cadence . Name == null || cadence . Abilities == null ) continue ;
61- foreach ( var unlock in cadence . Abilities ) {
62- string key = $ "{ cadName } :{ unlock . Ability . Name } ";
63- if ( state . UnlockedAbilities . Contains ( key ) ) continue ;
64- if ( unlock . Requirements != null && CanAffordEventually ( state , unlock . Requirements ) ) {
65- foreach ( var req in unlock . Requirements ) FarmResource ( state , req . Item , req . Quantity ) ;
66- foreach ( var req in unlock . Requirements ) SubtractFromInventory ( state , req . Item . Name , req . Quantity ) ;
67- state . UnlockedAbilities . Add ( key ) ;
68- if ( unlock . Ability . Metadata ? . TryGetValue ( "MagicCapacity" , out var capStr ) == true && int . TryParse ( capStr , out var capVal ) )
69- state . MagicCapacity = Math . Max ( state . MagicCapacity , capVal ) ;
70- UpdateStats ( state ) ; return true ;
71- }
72- }
73- }
74- return false ;
75- }
76-
77- private void SubtractFromInventory ( SimulationState state , string itemName , long quantity )
78- {
79- if ( state . Inventory . TryGetValue ( itemName , out long current ) )
80- state . Inventory [ itemName ] = current - quantity ;
81- else
82- state . Inventory [ itemName ] = - quantity ;
83- }
84-
85- private long GetFromInventory ( SimulationState state , string itemName )
86- {
87- return state . Inventory . TryGetValue ( itemName , out long current ) ? current : 0 ;
88- }
89-
90- private bool CanAffordEventually ( SimulationState state , ItemQuantity [ ] requirements )
91- {
92- if ( requirements == null ) return true ;
93- foreach ( var req in requirements ) {
94- if ( req . Item . Name == null ) continue ;
95- if ( GetFromInventory ( state , req . Item . Name ) >= req . Quantity ) continue ;
96- var source = GetBestSource ( state , req . Item ) ;
97- if ( source . Quest ? . Name == null && source . Ability ? . Name == null ) return false ;
98- }
99- return true ;
100- }
101-
102- private List < ( Quest Quest , QuestDetail Detail ) > GetAvailableQuests ( SimulationState state )
103- {
104- var available = new List < ( Quest , QuestDetail ) > ( ) ;
105- foreach ( var loc in locations . All ) {
106- if ( ! string . IsNullOrEmpty ( loc . RequiredQuest ) && ! state . CompletedQuests . Contains ( loc . RequiredQuest ) ) continue ;
107- if ( loc . Quests == null ) continue ;
108- foreach ( var q in loc . Quests ) {
109- if ( questUnlocks [ q ] ? . Any ( req => ! state . CompletedQuests . Contains ( req . Name ) ) ?? false ) continue ;
110- var det = questDetails [ q ] ;
111- if ( det . RequiredStats ? . All ( rs => state . CurrentStats . GetValueOrDefault ( rs . Key , 0 ) >= rs . Value ) ?? true ) available . Add ( ( q , det ) ) ;
112- }
113- }
114- return available ;
115- }
116-
117- private void ExecuteQuest ( SimulationState state , Quest q , QuestDetail detail )
118- {
119- if ( detail . Requirements != null ) {
120- foreach ( var req in detail . Requirements ) FarmResource ( state , req . Item , req . Quantity ) ;
121- foreach ( var req in detail . Requirements ) SubtractFromInventory ( state , req . Item . Name , req . Quantity ) ;
122- }
123- state . CurrentTime += detail . DurationSeconds * Math . Pow ( 0.75 , ( state . CurrentStats . GetValueOrDefault ( detail . PrimaryStat ?? "Vitality" , 10 ) - 10 ) / 10.0 ) ;
124- state . CompletedQuests . Add ( q . Name ) ;
125- if ( detail . Rewards != null ) foreach ( var rew in detail . Rewards ) state . Inventory [ rew . Item . Name ] = GetFromInventory ( state , rew . Item . Name ) + rew . Quantity ;
126- var cads = questToCadenceUnlocks [ q ] ;
127- if ( cads != null ) foreach ( var cad in cads ) state . UnlockedCadences . Add ( cad . Name ) ;
128- UpdateStats ( state ) ;
129- }
130-
131- private bool FarmResource ( SimulationState state , Item item , long quantityNeeded )
132- {
133- if ( _farmingStack . Contains ( item . Name ) ) return false ;
134- _farmingStack . Add ( item . Name ) ;
135- try {
136- long current = GetFromInventory ( state , item . Name ) ;
137- if ( current >= quantityNeeded ) return true ;
138- var source = GetBestSource ( state , item ) ;
139- var sq = source . Quest ; var sd = source . Detail ;
140- if ( sq ? . Name != null && sd ? . DurationSeconds > 0 ) {
141- var rewards = ( ( QuestDetail ) sd ) . Rewards ;
142- if ( rewards != null ) {
143- var reward = rewards . First ( r => r . Item == item ) ;
144- int runs = ( int ) Math . Min ( 1000 , Math . Ceiling ( ( double ) ( quantityNeeded - current ) / reward . Quantity ) ) ;
145- for ( int i = 0 ; i < runs ; i ++ ) ExecuteQuest ( state , ( Quest ) sq , ( QuestDetail ) sd ) ;
146- return GetFromInventory ( state , item . Name ) >= quantityNeeded ;
147- }
148- } else if ( source . Ability ? . Name != null && source . Recipes != null ) {
149- var recipe = source . Recipes . First ( x => x . Value . OutputItem == item ) ;
150- int runs = ( int ) Math . Min ( 1000 , Math . Ceiling ( ( double ) ( quantityNeeded - current ) / recipe . Value . OutputQuantity ) ) ;
151- for ( int i = 0 ; i < runs ; i ++ ) ExecuteRefinement ( state , ( CadenceAbility ) source . Ability , ( string ) source . PrimaryStat ! , recipe . Key , recipe . Value ) ;
152- return GetFromInventory ( state , item . Name ) >= quantityNeeded ;
153- }
154- return false ;
155- } finally { _farmingStack . Remove ( item . Name ) ; }
156- }
157-
158- private void ExecuteRefinement ( SimulationState state , CadenceAbility ability , string primaryStat , Item input , Recipe recipe )
159- {
160- FarmResource ( state , input , recipe . InputQuantity ) ;
161- SubtractFromInventory ( state , input . Name , recipe . InputQuantity ) ;
162- state . CurrentTime += 15.0 * Math . Pow ( 0.75 , ( state . CurrentStats . GetValueOrDefault ( primaryStat , 10 ) - 10 ) / 10.0 ) ;
163- state . Inventory [ recipe . OutputItem . Name ] = GetFromInventory ( state , recipe . OutputItem . Name ) + recipe . OutputQuantity ;
164- UpdateStats ( state ) ;
165- }
166-
167- private ActivitySource GetBestSource ( SimulationState state , Item item )
168- {
169- var recurring = locations . All . SelectMany ( l => l . Quests ?? Array . Empty < Quest > ( ) ) . Where ( q => {
170- var loc = locations . All . First ( x => x . Quests != null && x . Quests . Contains ( q ) ) ;
171- if ( ! string . IsNullOrEmpty ( loc . RequiredQuest ) && ! state . CompletedQuests . Contains ( loc . RequiredQuest ) ) return false ;
172- if ( questUnlocks [ q ] ? . Any ( req => ! state . CompletedQuests . Contains ( req . Name ) ) ?? false ) return false ;
173- var d = questDetails [ q ] ; return d . Type == QuestType . Recurring && ( d . Rewards ? . Any ( r => r . Item == item ) ?? false ) ;
174- } ) . OrderByDescending ( q => ( double ) ( questDetails [ q ] . Rewards ? . First ( r => r . Item == item ) . Quantity ?? 0 ) / questDetails [ q ] . DurationSeconds ) . FirstOrDefault ( ) ;
175- if ( recurring . Name != null ) return new ActivitySource { Quest = recurring , Detail = questDetails [ recurring ] } ;
176- var refMatch = refinements . ByKey . Where ( r => state . UnlockedAbilities . Any ( ua => ua . EndsWith ( $ ":{ r . Key . Name } ") ) && ( r . Value . Recipes ? . Values . Any ( rec => rec . OutputItem == item ) ?? false ) ) . FirstOrDefault ( ) ;
177- if ( refMatch . Key . Name != null ) return new ActivitySource { Ability = refMatch . Key , PrimaryStat = refMatch . Value . PrimaryStat , Recipes = refMatch . Value . Recipes } ;
178- return new ActivitySource ( ) ;
179- }
180-
181- private void UpdateStats ( SimulationState state )
182- {
183- if ( state . CurrentStats . GetValueOrDefault ( "Strength" , 0 ) >= 60 ) state . UnlockedCadences . Add ( "Geologist" ) ;
184- if ( state . CurrentStats . GetValueOrDefault ( "Speed" , 0 ) >= 60 ) state . UnlockedCadences . Add ( "Tide-Caller" ) ;
185- if ( state . CurrentStats . GetValueOrDefault ( "Vitality" , 0 ) >= 60 ) state . UnlockedCadences . Add ( "The Sentinel" ) ;
186- if ( state . CurrentStats . GetValueOrDefault ( "Magic" , 0 ) >= 100 ) state . UnlockedCadences . Add ( "Scholar" ) ;
187- if ( state . CurrentStats . GetValueOrDefault ( "Strength" , 0 ) >= 100 && state . CurrentStats . GetValueOrDefault ( "Speed" , 0 ) >= 100 ) state . UnlockedCadences . Add ( "Slayer" ) ;
188-
189- foreach ( var stat in stats . All ) {
190- string jAbil = stat . Name switch { "Strength" => "J-Str" , "Magic" => "J-Magic" , "Vitality" => "J-Vit" , "Speed" => "J-Speed" , _ => "J-" + stat . Name } ;
191- if ( state . UnlockedAbilities . Any ( ua => ua . EndsWith ( $ ":{ jAbil } ") ) ) {
192- var best = items . All . Where ( i => i . ItemType == ItemType . Spell && GetFromInventory ( state , i . Name ) > 0 )
193- . Select ( i => ( Item : i , Augment : statAugments [ i ] . FirstOrDefault ( a => a . Stat . Name == stat . Name ) ) )
194- . Where ( x => x . Augment . Stat . Name != null ) . OrderByDescending ( x => x . Augment . ModifierAtFull ) . FirstOrDefault ( ) ;
195- if ( best . Item . Name != null ) {
196- int val = 10 + ( int ) ( state . MagicCapacity * ( best . Augment . ModifierAtFull / 100.0 ) ) ;
197- state . CurrentStats [ stat . Name ] = Math . Max ( state . CurrentStats . GetValueOrDefault ( stat . Name , 25 ) , Math . Min ( 255 , val ) ) ;
198- }
199- }
31+ if ( ! state . CompletedQuests . Contains ( END_QUEST ) ) {
32+ Console . WriteLine ( "[FAIL] End Game node never reached." ) ;
33+ var endQuestObj = quests . All . First ( q => q . Name == END_QUEST ) ;
34+ var endQuestDet = questDetails [ endQuestObj ] ;
35+ Console . WriteLine ( $ "[DEBUG] End Game Stats Required: { string . Join ( ", " , endQuestDet . RequiredStats ? . Select ( kvp => $ "{ kvp . Key } : { kvp . Value } ") ?? [ "None" ] ) } ") ;
36+ Console . WriteLine ( $ "[DEBUG] Current Stats: { string . Join ( ", " , state . CurrentStats . Select ( kvp => $ "{ kvp . Key } : { kvp . Value } ") ) } ") ;
37+ Console . WriteLine ( $ "[DEBUG] Magic Capacity: { state . MagicCapacity } ") ;
20038 }
20139 }
20240}
0 commit comments