146 lines
4.2 KiB
Dart
146 lines
4.2 KiB
Dart
// lib/features/tournament/presentation/provider/shuffle_provider.dart
|
||
|
||
import 'dart:math';
|
||
import 'package:collection/collection.dart';
|
||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
import '../../data/models/player_model.dart';
|
||
import 'active_players_provider.dart';
|
||
import 'shuffle_settings_provider.dart';
|
||
|
||
class ShuffleResult {
|
||
final List<List<PlayerModel>> fields; // 3 Felder à max 12
|
||
final List<PlayerModel> bench;
|
||
final String message;
|
||
|
||
ShuffleResult(this.fields, this.bench, {this.message = ''});
|
||
}
|
||
|
||
// Dies ist jetzt ein STATEFUL Provider → wird nur bei explizitem Aufruf neu berechnet
|
||
final shuffleResultProvider =
|
||
StateProvider<ShuffleResult>((ref) => ShuffleResult([], [], message: 'Noch nicht gemischt'));
|
||
|
||
final shuffleControllerProvider = Provider((ref) => ShuffleController(ref));
|
||
|
||
class ShuffleController {
|
||
final Ref _ref;
|
||
ShuffleController(this._ref);
|
||
|
||
/// Hauptfunktion – wird nur vom Button aufgerufen
|
||
void shuffle() {
|
||
final activePlayers = _ref.read(activePlayersProvider);
|
||
final settings = _ref.read(shuffleSettingsProvider);
|
||
|
||
if (activePlayers.isEmpty) {
|
||
_ref.read(shuffleResultProvider.notifier).state =
|
||
ShuffleResult([], [], message: 'Keine aktiven Spieler');
|
||
return;
|
||
}
|
||
|
||
final result = ShuffleAlgorithm().run(activePlayers, settings);
|
||
|
||
// Jetzt erst speichern (einmalig!)
|
||
_commitAllChanges(result);
|
||
|
||
// UI aktualisieren
|
||
_ref.read(shuffleResultProvider.notifier).state = result;
|
||
}
|
||
|
||
void _commitAllChanges(ShuffleResult result) {
|
||
// Aussetzer markieren
|
||
for (final p in result.bench) {
|
||
p.hasSatOut = true;
|
||
p.save();
|
||
}
|
||
|
||
// Alle anderen zurücksetzen (falls nötig)
|
||
final allActive = _ref.read(activePlayersProvider);
|
||
for (final p in allActive) {
|
||
if (!result.bench.contains(p)) {
|
||
p.hasSatOut = false;
|
||
p.save();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
class ShuffleAlgorithm {
|
||
final Random _random = Random();
|
||
|
||
ShuffleResult run(List<PlayerModel> players, ShuffleSettings settings) {
|
||
// 1. Aussetzer bestimmen
|
||
final bench = _determineBench(players);
|
||
final playing = players.where((p) => !bench.contains(p)).toList();
|
||
|
||
// 2. Mischen
|
||
List<PlayerModel> ordered;
|
||
switch (settings.mode) {
|
||
case ShuffleMode.random:
|
||
ordered = playing..shuffle(_random);
|
||
break;
|
||
default:
|
||
ordered = _balancedShuffle(playing, settings);
|
||
break;
|
||
}
|
||
|
||
// 3. Auf 3 Felder verteilen
|
||
final fields = <List<PlayerModel>>[];
|
||
for (int i = 0; i < 3; i++) {
|
||
final start = i * 12;
|
||
final end = (start + 12).clamp(0, ordered.length);
|
||
fields.add(start < ordered.length ? ordered.sublist(start, end) : []);
|
||
}
|
||
|
||
return ShuffleResult(
|
||
fields,
|
||
bench,
|
||
message: 'Gemischte Teams (${ordered.length} Spieler)',
|
||
);
|
||
}
|
||
|
||
List<PlayerModel> _determineBench(List<PlayerModel> players) {
|
||
if (players.length <= 36) return [];
|
||
|
||
final excess = players.length - 36;
|
||
final never = players.where((p) => !p.hasSatOut).toList();
|
||
final already = players.where((p) => p.hasSatOut).toList();
|
||
|
||
final bench = <PlayerModel>[];
|
||
bench.addAll(never.take(excess));
|
||
if (bench.length < excess) {
|
||
bench.addAll(already.take(excess - bench.length));
|
||
}
|
||
return bench;
|
||
}
|
||
|
||
List<PlayerModel> _balancedShuffle(List<PlayerModel> players, ShuffleSettings settings) {
|
||
players.sort((a, b) {
|
||
if (settings.prioritizeGenderBalance) {
|
||
final g = a.gender.index.compareTo(b.gender.index);
|
||
if (g != 0) return g;
|
||
}
|
||
if (settings.prioritizeLevelBalance) {
|
||
final l = b.skillLevel.index.compareTo(a.skillLevel.index);
|
||
if (l != 0) return l;
|
||
}
|
||
if (settings.usePositions) {
|
||
final az = a.positions.contains(Position.zuspieler) ? 1 : 0;
|
||
final bz = b.positions.contains(Position.zuspieler) ? 1 : 0;
|
||
if (az != bz) return bz.compareTo(az);
|
||
}
|
||
return 0;
|
||
});
|
||
|
||
final teams = List.generate(6, (_) => <PlayerModel>[]);
|
||
for (int i = 0; i < players.length; i++) {
|
||
teams[i % 6].add(players[i]);
|
||
}
|
||
for (final t in teams) t.shuffle(_random);
|
||
|
||
final result = <PlayerModel>[];
|
||
for (int i = 0; i < 3; i++) {
|
||
result.addAll(teams[i]);
|
||
result.addAll(teams[i + 3]);
|
||
}
|
||
return result;
|
||
}
|
||
} |