Skip to content

Commit 58e69cf

Browse files
committed
Do a BIG work on trainings. Training Analyzer, engine, UI...
1 parent 8055aa1 commit 58e69cf

39 files changed

+1452
-676
lines changed
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
import 'dart:convert';
2+
import 'dart:io';
3+
4+
import 'package:equatable/equatable.dart';
5+
import 'package:flutter/material.dart';
6+
import 'package:flutter_bloc/flutter_bloc.dart';
7+
import 'package:path/path.dart' as path;
8+
9+
import 'package:sj_manager/bloc/training_analyzer/training_test_runner.dart';
10+
import 'package:sj_manager/json/json_types.dart';
11+
import 'package:sj_manager/models/simulation/flow/dynamic_params/jumper_dynamic_params.dart';
12+
import 'package:sj_manager/models/simulation/flow/training/jumper_training_config.dart';
13+
import 'package:sj_manager/models/training_analyzer/actions.dart';
14+
import 'package:sj_manager/models/training_analyzer/chart_data_category.dart';
15+
import 'package:sj_manager/models/training_analyzer/training_analyzer_result.dart';
16+
import 'package:sj_manager/models/training_analyzer/training_segment.dart';
17+
import 'package:sj_manager/models/user_db/jumper/jumper_skills.dart';
18+
import 'package:sj_manager/models/user_db/psyche/level_of_consciousness.dart';
19+
import 'package:sj_manager/training_engine/jumper_training_engine_settings.dart';
20+
import 'package:sj_manager/utils/file_system.dart';
21+
import 'package:sj_manager/utils/training_analyzer_utils.dart';
22+
23+
class TrainingAnalyzerCubit extends Cubit<TrainingAnalyzerNotSimulated> {
24+
TrainingAnalyzerCubit()
25+
: super(TrainingAnalyzerNotSimulated(
26+
dataCategories: TrainingAnalyzerDataCategory.values.toSet()));
27+
28+
void simulate(
29+
{required PlarformSpecificPathsCache pathsCache,
30+
Set<TrainingAnalyzerActions> additionalActions = const {}}) {
31+
late final Json configJson;
32+
try {
33+
final configFile =
34+
userDataFile(pathsCache, path.join('training_analyzer', 'config.json'));
35+
configJson = jsonDecode(configFile.readAsStringSync()) as Json;
36+
} catch (e) {
37+
debugPrint(
38+
'Błąd podczas wczytywania konfiguracji (config.json). Oryginalna treść błędu: $e');
39+
rethrow;
40+
}
41+
final engineSettingsJson = configJson['engineSettings'] as Json;
42+
final dynamicParamsJson = configJson['dynamicParams'] as Json;
43+
final skillsJson = configJson['jumperSkills'] as Json;
44+
final segmentsJson = (configJson['trainingSegments'] as List).cast<Json>();
45+
46+
final engineSettings = JumperTrainingEngineSettings.fromJson(engineSettingsJson);
47+
final jumperSkills = JumperSkills.fromJson(skillsJson);
48+
49+
final dynamicParams = JumperDynamicParams(
50+
trainingConfig: null,
51+
form: (dynamicParamsJson['form'] as num).toDouble(),
52+
trainingFeeling: (dynamicParamsJson['trainingFeeling'] as Map).map(
53+
(categoryName, value) => MapEntry(
54+
JumperTrainingCategory.values.singleWhere((cat) => cat.name == categoryName),
55+
(value as num).toDouble(),
56+
),
57+
),
58+
jumpsConsistency: (dynamicParamsJson['jumpsConsistency'] as num).toDouble(),
59+
morale: (dynamicParamsJson['morale'] as num).toDouble(),
60+
fatigue: (dynamicParamsJson['fatigue'] as num).toDouble(),
61+
levelOfConsciousness: LevelOfConsciousness.fromMapOfConsciousness(
62+
LevelOfConsciousnessLabels.values.singleWhere(
63+
(lbl) => lbl.name == dynamicParamsJson['levelOfConsciousness'],
64+
),
65+
),
66+
);
67+
68+
final trainingSegments = segmentsJson.map((segmentJson) => TrainingSegment(
69+
start: segmentJson['start'],
70+
end: segmentJson['end'],
71+
scale: (segmentJson['scale'] as num).toDouble(),
72+
trainingConfig: JumperTrainingConfig(
73+
balance: (segmentJson['trainingConfig']['balance'] as Map).map(
74+
(categoryName, value) => MapEntry(
75+
JumperTrainingCategory.values
76+
.singleWhere((value) => value.name == categoryName),
77+
(value as num).toDouble(),
78+
),
79+
),
80+
),
81+
));
82+
83+
final runner = TrainingTestRunner(
84+
segments: trainingSegments.toList(),
85+
dynamicParams: dynamicParams,
86+
jumperSkills: jumperSkills,
87+
engineSettings: engineSettings,
88+
daysToSimulate: configJson['daysToSimulate'],
89+
);
90+
91+
final result = runner.run();
92+
93+
if (additionalActions.contains(TrainingAnalyzerActions.saveFormattedTxt)) {
94+
final buffer = StringBuffer();
95+
for (var day = 1; day <= result.dayResults.length; day++) {
96+
final dayResult = result.dayResults[day - 1];
97+
buffer.writeln(
98+
'Dzień $day: ${formatJumperTrainingResultResultForAnalyzer(dayResult.trainingResult)}');
99+
}
100+
final resultsFile = userDataFile(
101+
pathsCache, path.join('training_analyzer', 'formatted_results.txt'));
102+
resultsFile.writeAsStringSync(buffer.toString());
103+
}
104+
if (additionalActions.contains(TrainingAnalyzerActions.saveCsv)) {
105+
final resultsFile =
106+
userDataFile(pathsCache, path.join('training_analyzer', 'results.csv'));
107+
final csv = TrainingAnalyzerDaySimulationResult.listToCsv(result.dayResults,
108+
delimiter: ';');
109+
resultsFile.writeAsStringSync(csv);
110+
}
111+
if (additionalActions.contains(TrainingAnalyzerActions.saveAttributeStats)) {
112+
_writeAttributeStats(
113+
result.dayResults
114+
.map((result) => result.trainingResult.skills.takeoffQuality)
115+
.toList(),
116+
userDataFile(pathsCache, path.join('training_analyzer', 'takeoff_stats.txt')),
117+
);
118+
_writeAttributeStats(
119+
result.dayResults
120+
.map((result) => result.trainingResult.skills.flightQuality)
121+
.toList(),
122+
userDataFile(pathsCache, path.join('training_analyzer', 'flight_stats.txt')),
123+
);
124+
_writeAttributeStats(
125+
result.dayResults
126+
.map((result) => result.trainingResult.skills.landingQuality)
127+
.toList(),
128+
userDataFile(pathsCache, path.join('training_analyzer', 'landing_stats.txt')),
129+
);
130+
_writeAttributeStats(
131+
result.dayResults.map((result) => result.trainingResult.form).toList(),
132+
userDataFile(pathsCache, path.join('training_analyzer', 'form_stats.txt')),
133+
);
134+
}
135+
136+
emit(
137+
TrainingAnalyzerSimulated(
138+
result: result,
139+
dataCategories: state.dataCategories,
140+
),
141+
);
142+
}
143+
144+
void _writeAttributeStats(List<double> values, File file) {
145+
final buffer = StringBuffer();
146+
147+
var deltas = <double>[];
148+
for (int i = 0; i < values.length - 1; i++) {
149+
deltas.add(values[i + 1] - values[i]);
150+
}
151+
deltas.sort((a, b) => b.abs().compareTo(a.abs()));
152+
153+
final deltasMaxRange = 25.clamp(0, values.length);
154+
buffer.writeln('TOP $deltasMaxRange zmian wartości:');
155+
for (var i = 0; i < deltasMaxRange; i++) {
156+
final delta = deltas[i];
157+
var furtherText = delta != 0 ? delta.abs().toStringAsFixed(2) : '';
158+
buffer.writeln('${i + 1}. $furtherText');
159+
}
160+
161+
final sortedForm = List.of(values)..sort((first, second) => second.compareTo(first));
162+
final highFormMaxRange = 25.clamp(0, values.length);
163+
buffer.writeln('\n\nTOP $highFormMaxRange najwyższych wartości');
164+
for (var i = 0; i < highFormMaxRange; i++) {
165+
final delta = sortedForm[i];
166+
var furtherText = delta != 0 ? delta.abs().toStringAsFixed(2) : '';
167+
buffer.writeln('${i + 1}. $furtherText');
168+
}
169+
170+
sortedForm.sort((first, second) => first.compareTo(second));
171+
final lowFormMaxRange = 25.clamp(0, values.length);
172+
buffer.writeln('\n\nTOP $lowFormMaxRange najniższych wartości');
173+
for (var i = 0; i < lowFormMaxRange; i++) {
174+
final delta = sortedForm[i];
175+
var furtherText = delta != 0 ? delta.abs().toStringAsFixed(2) : '';
176+
buffer.writeln('${i + 1}. $furtherText');
177+
}
178+
179+
file.writeAsStringSync(buffer.toString());
180+
}
181+
182+
void setCategories(Set<TrainingAnalyzerDataCategory> categories) {
183+
if (state.runtimeType == TrainingAnalyzerNotSimulated) {
184+
emit(state.copyWith(dataCategories: categories));
185+
} else if (state.runtimeType == TrainingAnalyzerSimulated) {
186+
emit((state as TrainingAnalyzerSimulated).copyWith(
187+
dataCategories: categories,
188+
));
189+
}
190+
}
191+
}
192+
193+
class TrainingAnalyzerNotSimulated with EquatableMixin {
194+
const TrainingAnalyzerNotSimulated({
195+
required this.dataCategories,
196+
});
197+
198+
final Set<TrainingAnalyzerDataCategory> dataCategories;
199+
200+
@override
201+
List<Object?> get props => [
202+
dataCategories,
203+
];
204+
205+
TrainingAnalyzerNotSimulated copyWith({
206+
Set<TrainingAnalyzerDataCategory>? dataCategories,
207+
}) {
208+
return TrainingAnalyzerNotSimulated(
209+
dataCategories: dataCategories ?? this.dataCategories,
210+
);
211+
}
212+
}
213+
214+
class TrainingAnalyzerSimulated extends TrainingAnalyzerNotSimulated {
215+
const TrainingAnalyzerSimulated({
216+
required this.result,
217+
required super.dataCategories,
218+
});
219+
220+
final TrainingAnalyzerResult result;
221+
222+
@override
223+
List<Object?> get props => [
224+
result,
225+
super.props,
226+
];
227+
228+
@override
229+
TrainingAnalyzerSimulated copyWith({
230+
TrainingAnalyzerResult? result,
231+
Set<TrainingAnalyzerDataCategory>? dataCategories,
232+
}) {
233+
return TrainingAnalyzerSimulated(
234+
result: result ?? this.result,
235+
dataCategories: dataCategories ?? this.dataCategories,
236+
);
237+
}
238+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import 'package:sj_manager/models/training_analyzer/training_analyzer_result.dart';
2+
import 'package:sj_manager/models/training_analyzer/training_segment.dart';
3+
import 'package:sj_manager/models/simulation/flow/dynamic_params/jumper_dynamic_params.dart';
4+
import 'package:sj_manager/models/user_db/jumper/jumper_skills.dart';
5+
import 'package:sj_manager/training_engine/jumper_training_engine.dart';
6+
import 'package:sj_manager/training_engine/jumper_training_engine_settings.dart';
7+
8+
class TrainingTestRunner {
9+
const TrainingTestRunner({
10+
required this.segments,
11+
required this.dynamicParams,
12+
required this.jumperSkills,
13+
required this.engineSettings,
14+
required this.daysToSimulate,
15+
});
16+
17+
final List<TrainingSegment> segments;
18+
final JumperDynamicParams dynamicParams;
19+
final JumperSkills jumperSkills;
20+
final JumperTrainingEngineSettings engineSettings;
21+
final int daysToSimulate;
22+
23+
TrainingAnalyzerResult run() {
24+
final dayResults = <TrainingAnalyzerDaySimulationResult>[];
25+
var currentDynamicParams = dynamicParams;
26+
var currentJumperSkills = jumperSkills;
27+
TrainingAnalyzerDaySimulationResult? lastResult;
28+
29+
for (var day = 1; day < daysToSimulate + 1; day++) {
30+
final applicableSegment = segments.cast<TrainingSegment?>().firstWhere(
31+
(segment) => segment!.start <= day && segment.end >= day,
32+
orElse: () => null,
33+
);
34+
if (applicableSegment == null) {
35+
if (lastResult != null) {
36+
dayResults.add(lastResult);
37+
continue;
38+
} else {
39+
throw StateError('There is no training segment in passed list. Hmm...');
40+
}
41+
}
42+
final engine = JumperTrainingEngine(
43+
settings: engineSettings,
44+
jumperSkills: currentJumperSkills,
45+
dynamicParams: currentDynamicParams.copyWith(
46+
trainingConfig: applicableSegment.trainingConfig,
47+
),
48+
scaleFactor: applicableSegment.scale,
49+
);
50+
final trainingResult = engine.doTraining();
51+
lastResult = TrainingAnalyzerDaySimulationResult(
52+
day: day,
53+
trainingResult: trainingResult,
54+
);
55+
currentDynamicParams = currentDynamicParams.copyWith(
56+
form: trainingResult.form,
57+
trainingFeeling: trainingResult.trainingFeeling,
58+
jumpsConsistency: trainingResult.jumpsConsistency,
59+
fatigue: trainingResult.fatigue,
60+
);
61+
currentJumperSkills = trainingResult.skills;
62+
dayResults.add(lastResult);
63+
}
64+
return TrainingAnalyzerResult(
65+
segments: segments,
66+
dayResults: dayResults,
67+
);
68+
}
69+
}

0 commit comments

Comments
 (0)