feat: basic integration of teams

This commit is contained in:
2026-05-17 21:29:16 +02:00
parent badf5ea311
commit a957408c7e
20 changed files with 1325 additions and 325 deletions

View File

@@ -16,6 +16,7 @@ class Match {
final Game game;
final Group? group;
final List<Player> players;
final bool isTeamMatch;
final List<Team>? teams;
final String notes;
Map<String, ScoreEntry?> scores;
@@ -26,6 +27,7 @@ class Match {
required this.players,
this.endedAt,
this.group,
this.isTeamMatch = false,
this.teams,
this.notes = '',
String? id,
@@ -37,7 +39,7 @@ class Match {
@override
String toString() {
return 'Match{id: $id, createdAt: $createdAt, endedAt: $endedAt, name: $name, game: $game, group: $group, players: $players, notes: $notes, scores: $scores, mvp: $mvp}';
return 'Match{id: $id, createdAt: $createdAt, endedAt: $endedAt, name: $name, game: $game, group: $group, players: $players, isTeamMatch: $isTeamMatch, teams: $teams, notes: $notes, scores: $scores, mvp: $mvp}';
}
Match copyWith({
@@ -48,6 +50,7 @@ class Match {
Game? game,
Group? group,
List<Player>? players,
bool? isTeamMatch,
List<Team>? teams,
String? notes,
Map<String, ScoreEntry?>? scores,
@@ -60,6 +63,7 @@ class Match {
game: game ?? this.game,
group: group ?? this.group,
players: players ?? this.players,
isTeamMatch: isTeamMatch ?? this.isTeamMatch,
teams: teams ?? this.teams,
notes: notes ?? this.notes,
scores: scores ?? this.scores,
@@ -78,6 +82,7 @@ class Match {
game == other.game &&
group == other.group &&
const DeepCollectionEquality().equals(players, other.players) &&
isTeamMatch == other.isTeamMatch &&
const DeepCollectionEquality().equals(teams, other.teams) &&
notes == other.notes &&
const DeepCollectionEquality().equals(scores, other.scores);
@@ -91,6 +96,7 @@ class Match {
game,
group,
const DeepCollectionEquality().hash(players),
isTeamMatch,
const DeepCollectionEquality().hash(teams),
notes,
const DeepCollectionEquality().hash(scores),
@@ -112,6 +118,7 @@ class Match {
),
group = null,
players = [],
isTeamMatch = json['isTeamMatch'],
teams = [],
scores = json['scores'] != null
? (json['scores'] as Map<String, dynamic>).map(
@@ -133,11 +140,13 @@ class Match {
'gameId': game.id,
'groupId': group?.id,
'playerIds': players.map((player) => player.id).toList(),
'isTeamMatch': isTeamMatch,
'teams': teams?.map((team) => team.toJson()).toList(),
'scores': scores.map((key, value) => MapEntry(key, value?.toJson())),
'notes': notes,
};
// Most Valuable Player(s) based on the match's ruleset
List<Player> get mvp {
if (players.isEmpty || scores.isEmpty) return [];
@@ -195,4 +204,49 @@ class Match {
return playerScore.score == lowestScore;
}).toList();
}
// MVP for team-based matches (Most Valuable Team)
List<Team> get mvt {
if (teams == null || teams!.isEmpty) return [];
switch (game.ruleset) {
case Ruleset.highestScore:
return _getHighestScoreTeam();
case Ruleset.lowestScore:
return _getLowestScoreTeam();
case Ruleset.singleWinner:
return _getHighestScoreTeam().take(1).toList();
case Ruleset.singleLoser:
return _getLowestScoreTeam().take(1).toList();
case Ruleset.multipleWinners:
return _getHighestScoreTeam();
case Ruleset.placement:
return _getHighestScoreTeam().take(1).toList();
}
}
List<Team> _getHighestScoreTeam() {
final int highestScore = teams!
.map((team) => team.score)
.reduce((max, score) => score > max ? score : max);
return teams!.where((team) {
return team.score == highestScore;
}).toList();
}
List<Team> _getLowestScoreTeam() {
final int lowestScore = teams!
.map((team) => team.score)
.reduce((min, score) => score < min ? score : min);
return teams!.where((team) {
return team.score == lowestScore;
}).toList();
}
}

View File

@@ -1,5 +1,6 @@
import 'package:clock/clock.dart';
import 'package:collection/collection.dart';
import 'package:tallee/core/enums.dart';
import 'package:tallee/data/models/player.dart';
import 'package:uuid/uuid.dart';
@@ -7,31 +8,39 @@ class Team {
final String id;
final String name;
final DateTime createdAt;
final GameColor color;
final int score;
final List<Player> members;
Team({
String? id,
required this.name,
DateTime? createdAt,
this.color = GameColor.blue,
this.score = 0,
required this.members,
}) : id = id ?? const Uuid().v4(),
createdAt = createdAt ?? clock.now();
@override
String toString() {
return 'Team{id: $id, name: $name, members: $members}';
return 'Team{id: $id, name: $name, color: $color, score: $score, members: $members}';
}
Team copyWith({
String? id,
String? name,
DateTime? createdAt,
GameColor? color,
int? score,
List<Player>? members,
}) {
return Team(
id: id ?? this.id,
name: name ?? this.name,
createdAt: createdAt ?? this.createdAt,
color: color ?? this.color,
score: score ?? this.score,
members: members ?? this.members,
);
}
@@ -44,6 +53,8 @@ class Team {
id == other.id &&
name == other.name &&
createdAt == other.createdAt &&
color == other.color &&
score == other.score &&
const DeepCollectionEquality().equals(members, other.members);
@override
@@ -51,6 +62,8 @@ class Team {
id,
name,
createdAt,
color,
score,
const DeepCollectionEquality().hash(members),
);
@@ -58,12 +71,16 @@ class Team {
: id = json['id'],
name = json['name'],
createdAt = DateTime.parse(json['createdAt']),
color = GameColor.values.byName(json['color'] ?? GameColor.blue.name),
score = json['score'] ?? 0,
members = []; // Populated during import via DataTransferService
Map<String, dynamic> toJson() => {
'id': id,
'name': name,
'createdAt': createdAt.toIso8601String(),
'color': color.name,
'score': score,
'memberIds': members.map((member) => member.id).toList(),
};
}