From 6b2651a3966887c40d19ac678d0d6509ea4cab35 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Fri, 22 Aug 2025 14:09:09 +0200 Subject: [PATCH] First version of inserting into db --- lib/data/dao/game_session_dao.dart | 88 +++++++++++ lib/data/{db => }/dao/game_session_dao.g.dart | 0 lib/data/{db => }/dao/player_dao.dart | 18 +++ lib/data/{db => }/dao/player_dao.g.dart | 0 lib/data/{db => }/dao/round_scores_dao.dart | 14 +- lib/data/{db => }/dao/round_scores_dao.g.dart | 0 lib/data/dao/rounds_dao.dart | 140 ++++++++++++++++++ lib/data/{db => }/dao/rounds_dao.g.dart | 2 + lib/data/db/dao/game_session_dao.dart | 39 ----- lib/data/db/dao/rounds_dao.dart | 39 ----- lib/data/db/database.dart | 8 +- lib/data/dto/game_session.dart | 41 ++--- lib/data/dto/player.dart | 2 +- .../home/active_game/active_game_view.dart | 30 ++-- .../views/home/active_game/graph_view.dart | 2 +- .../views/home/active_game/points_view.dart | 26 ++-- .../views/home/active_game/round_view.dart | 19 +-- .../views/home/create_game_view.dart | 25 +++- test/data/game_session_test.dart | 24 ++- 19 files changed, 365 insertions(+), 152 deletions(-) create mode 100644 lib/data/dao/game_session_dao.dart rename lib/data/{db => }/dao/game_session_dao.g.dart (100%) rename lib/data/{db => }/dao/player_dao.dart (70%) rename lib/data/{db => }/dao/player_dao.g.dart (100%) rename lib/data/{db => }/dao/round_scores_dao.dart (67%) rename lib/data/{db => }/dao/round_scores_dao.g.dart (100%) create mode 100644 lib/data/dao/rounds_dao.dart rename lib/data/{db => }/dao/rounds_dao.g.dart (78%) delete mode 100644 lib/data/db/dao/game_session_dao.dart delete mode 100644 lib/data/db/dao/rounds_dao.dart diff --git a/lib/data/dao/game_session_dao.dart b/lib/data/dao/game_session_dao.dart new file mode 100644 index 0000000..2c12968 --- /dev/null +++ b/lib/data/dao/game_session_dao.dart @@ -0,0 +1,88 @@ +import 'package:cabo_counter/data/db/database.dart'; +import 'package:cabo_counter/data/db/tables/game_session_table.dart'; +import 'package:cabo_counter/data/dto/game_session.dart'; +import 'package:cabo_counter/data/dto/player.dart'; +import 'package:cabo_counter/data/dto/round.dart'; +import 'package:drift/drift.dart'; + +part 'game_session_dao.g.dart'; + +@DriftAccessor(tables: [GameSessionTable]) +class GameSessionDao extends DatabaseAccessor + with _$GameSessionDaoMixin { + GameSessionDao(super.db); + + /// Retrieves a game session by its ID. + Future getGameSession(String id) async { + final query = select(gameSessionTable)..where((tbl) => tbl.id.equals(id)); + final gameSessionResult = await query.getSingle(); + + List playerList = await db.playerDao.getPlayersByGameId(id); + List roundList = await db.roundsDao.getRoundsByGameId(id); + + GameSession gameSession = GameSession( + id: gameSessionResult.id, + createdAt: gameSessionResult.createdAt, + gameTitle: gameSessionResult.gameTitle, + players: playerList, + pointLimit: gameSessionResult.pointLimit, + caboPenalty: gameSessionResult.caboPenalty, + isPointsLimitEnabled: gameSessionResult.isPointsLimitEnabled, + isGameFinished: gameSessionResult.isGameFinished, + winner: gameSessionResult.winner ?? '', + roundNumber: gameSessionResult.roundNumber, + roundList: roundList); + + return gameSession; + } + + /// Retrieves all game sessions from the database. + Future> getAllGameSessions() async { + final query = select(gameSessionTable); + final gameSessionResults = await query.get(); + + List gameSessions = await Future.wait( + gameSessionResults.map((row) async { + List playerList = await db.playerDao.getPlayersByGameId(row.id); + List roundList = await db.roundsDao.getRoundsByGameId(row.id); + + return GameSession( + id: row.id, + createdAt: row.createdAt, + gameTitle: row.gameTitle, + players: playerList, + pointLimit: row.pointLimit, + caboPenalty: row.caboPenalty, + isPointsLimitEnabled: row.isPointsLimitEnabled, + isGameFinished: row.isGameFinished, + winner: row.winner ?? '', + roundNumber: row.roundNumber, + roundList: roundList, + ); + }), + ); + + return gameSessions; + } + + Future insertGameSession(GameSession gameSession) async { + await into(gameSessionTable).insert( + GameSessionTableCompanion.insert( + id: gameSession.id, + createdAt: gameSession.createdAt, + gameTitle: gameSession.gameTitle, + pointLimit: gameSession.pointLimit, + caboPenalty: gameSession.caboPenalty, + isPointsLimitEnabled: gameSession.isPointsLimitEnabled, + isGameFinished: gameSession.isGameFinished, + winner: Value(gameSession.winner), + roundNumber: gameSession.roundNumber, + ), + ); + + db.playerDao.insertPlayers(gameSession.id, gameSession.players); + + db.roundsDao.insertMultipleRounds( + gameSession.id, gameSession.roundList, gameSession.players); + } +} diff --git a/lib/data/db/dao/game_session_dao.g.dart b/lib/data/dao/game_session_dao.g.dart similarity index 100% rename from lib/data/db/dao/game_session_dao.g.dart rename to lib/data/dao/game_session_dao.g.dart diff --git a/lib/data/db/dao/player_dao.dart b/lib/data/dao/player_dao.dart similarity index 70% rename from lib/data/db/dao/player_dao.dart rename to lib/data/dao/player_dao.dart index 0e5ca53..f3fd929 100644 --- a/lib/data/db/dao/player_dao.dart +++ b/lib/data/dao/player_dao.dart @@ -35,4 +35,22 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { return result.position; } + + /// Inserts a new player into the database. + Future insertPlayers(String gameId, List players) async { + await batch((batch) { + for (int i = 0; i < players.length; i++) { + batch.insert( + playerTable, + PlayerTableCompanion.insert( + playerId: players[i].playerId, + gameId: gameId, + name: players[i].name, + position: i, + totalScore: players[i].totalScore, + ), + ); + } + }); + } } diff --git a/lib/data/db/dao/player_dao.g.dart b/lib/data/dao/player_dao.g.dart similarity index 100% rename from lib/data/db/dao/player_dao.g.dart rename to lib/data/dao/player_dao.g.dart diff --git a/lib/data/db/dao/round_scores_dao.dart b/lib/data/dao/round_scores_dao.dart similarity index 67% rename from lib/data/db/dao/round_scores_dao.dart rename to lib/data/dao/round_scores_dao.dart index d87601c..336a7b9 100644 --- a/lib/data/db/dao/round_scores_dao.dart +++ b/lib/data/dao/round_scores_dao.dart @@ -11,7 +11,9 @@ class RoundScoresDao extends DatabaseAccessor RoundScoresDao(super.db); /// Retrieves all scores for a specific round by its ID. - Future> getRoundScoresByRoundId(String roundId) async { + /// This method returns a list of [RoundScore] objects sorted by player + /// position in the corresponding gameSession + Future> _getRoundScoresByRoundId(String roundId) async { final query = select(roundScoresTable) ..where((tbl) => tbl.roundId.equals(roundId)); @@ -37,14 +39,20 @@ class RoundScoresDao extends DatabaseAccessor }).toList(); } + /// Retrieves all scores for a specific round by its ID. + /// This method returns a list of scores sorted by player position in the + /// corresponding gameSession. Future> getScoresByRoundId(String roundId) async { - List roundScores = await getRoundScoresByRoundId(roundId); + List roundScores = await _getRoundScoresByRoundId(roundId); return roundScores.map((score) => score.score).toList(); } + /// Retrieves all score updates for a specific round by its ID. + /// This method returns a list of score updates sorted by player position in + /// the corresponding gameSession. Future> getScoreUpdatesByRoundId(String roundId) async { - List roundScores = await getRoundScoresByRoundId(roundId); + List roundScores = await _getRoundScoresByRoundId(roundId); return roundScores.map((score) => score.scoreUpdate).toList(); } diff --git a/lib/data/db/dao/round_scores_dao.g.dart b/lib/data/dao/round_scores_dao.g.dart similarity index 100% rename from lib/data/db/dao/round_scores_dao.g.dart rename to lib/data/dao/round_scores_dao.g.dart diff --git a/lib/data/dao/rounds_dao.dart b/lib/data/dao/rounds_dao.dart new file mode 100644 index 0000000..46e5a13 --- /dev/null +++ b/lib/data/dao/rounds_dao.dart @@ -0,0 +1,140 @@ +import 'package:cabo_counter/data/db/database.dart'; +import 'package:cabo_counter/data/db/tables/round_scores_table.dart'; +import 'package:cabo_counter/data/db/tables/rounds_table.dart'; +import 'package:cabo_counter/data/dto/player.dart'; +import 'package:cabo_counter/data/dto/round.dart'; +import 'package:drift/drift.dart'; +import 'package:uuid/uuid.dart'; + +part 'rounds_dao.g.dart'; + +@DriftAccessor(tables: [RoundsTable, RoundScoresTable]) +class RoundsDao extends DatabaseAccessor with _$RoundsDaoMixin { + RoundsDao(super.db); + + /// Retrieves all rounds for a specific game session by its ID. + Future> getRoundsByGameId(String gameId) async { + final query = select(roundsTable) + ..where((tbl) => tbl.gameId.equals(gameId)); + + final roundResult = await query.get(); + + final roundList = await Future.wait( + roundResult.map((row) async { + final scores = await db.roundScoresDao.getScoresByRoundId(row.roundId); + final roundScores = + await db.roundScoresDao.getScoreUpdatesByRoundId(row.roundId); + + return Round( + roundId: row.roundId, + gameId: row.gameId, + roundNum: row.roundNumber, + caboPlayerIndex: row.caboPlayerIndex, + kamikazePlayerIndex: row.kamikazePlayerIndex, + scores: scores, + scoreUpdates: roundScores, + ); + }), + ); + + return roundList; + } + + /// Retrieves a specific round by its [gameId] and [roundNumber]. + /// Returns null if the round does not exist. + Future getRoundByGameIdAndRoundNumber( + String gameId, int roundNumber) async { + final query = select(roundsTable) + ..where((tbl) => + tbl.gameId.equals(gameId) & tbl.roundNumber.equals(roundNumber)); + final roundResult = await query.getSingleOrNull(); + if (roundResult == null) return null; + + final scoreResult = await Future.wait([ + db.roundScoresDao.getScoresByRoundId(roundResult.roundId), + db.roundScoresDao.getScoreUpdatesByRoundId(roundResult.roundId), + ]); + + return Round( + roundId: roundResult.roundId, + gameId: roundResult.gameId, + roundNum: roundResult.roundNumber, + caboPlayerIndex: roundResult.caboPlayerIndex, + kamikazePlayerIndex: roundResult.kamikazePlayerIndex, + scores: scoreResult[0], + scoreUpdates: scoreResult[1], + ); + } + + /// Inserts a new round into the database. + /// This method creates a new round with a unique ID and inserts it + /// along with the scores for each player in the round. + /// [gameId] is the ID of the game session this round belongs to. + /// [round] is the round data to be inserted. + /// [players] is the list of players in the game session. + Future insertOneRound( + String gameId, Round round, List players) async { + var uuid = const Uuid(); + String roundId = uuid.v1(); + + final roundEntry = RoundsTableCompanion.insert( + roundId: roundId, + gameId: gameId, + roundNumber: round.roundNum, + caboPlayerIndex: round.caboPlayerIndex, + kamikazePlayerIndex: Value(round.kamikazePlayerIndex), + ); + + await into(roundsTable).insert(roundEntry); + + for (int i = 0; i < players.length; i++) { + final player = players[i]; + final roundScoreEntry = RoundScoresTableCompanion.insert( + roundId: roundId, + playerId: player.playerId, + score: round.scores[i], + scoreUpdate: round.scoreUpdates[i], + ); + await into(roundScoresTable).insert(roundScoreEntry); + } + } + + /// Inserts multiple rounds into the database. + /// This method uses a batch operation to insert all rounds and their scores + /// in a single transaction. + /// [gameId] is the ID of the game session these rounds belong to. + /// [rounds] is the list of rounds to be inserted. + /// [players] is the list of players in the game session. + Future insertMultipleRounds( + String gameId, List rounds, List players) async { + var uuid = const Uuid(); + + await batch((batch) { + final roundEntries = []; + final roundScoreEntries = []; + + for (final round in rounds) { + final roundId = uuid.v1(); + roundEntries.add(RoundsTableCompanion.insert( + roundId: roundId, + gameId: gameId, + roundNumber: round.roundNum, + caboPlayerIndex: round.caboPlayerIndex, + kamikazePlayerIndex: Value(round.kamikazePlayerIndex), + )); + + for (int i = 0; i < players.length; i++) { + roundScoreEntries.add(RoundScoresTableCompanion.insert( + roundId: roundId, + playerId: players[i].playerId, + score: round.scores[i], + scoreUpdate: round.scoreUpdates[i], + )); + } + } + + batch.insertAll(roundsTable, roundEntries); + batch.insertAll(roundScoresTable, roundScoreEntries); + }); + } +} diff --git a/lib/data/db/dao/rounds_dao.g.dart b/lib/data/dao/rounds_dao.g.dart similarity index 78% rename from lib/data/db/dao/rounds_dao.g.dart rename to lib/data/dao/rounds_dao.g.dart index 85166e6..506e5a4 100644 --- a/lib/data/db/dao/rounds_dao.g.dart +++ b/lib/data/dao/rounds_dao.g.dart @@ -7,4 +7,6 @@ mixin _$RoundsDaoMixin on DatabaseAccessor { $GameSessionTableTable get gameSessionTable => attachedDatabase.gameSessionTable; $RoundsTableTable get roundsTable => attachedDatabase.roundsTable; + $RoundScoresTableTable get roundScoresTable => + attachedDatabase.roundScoresTable; } diff --git a/lib/data/db/dao/game_session_dao.dart b/lib/data/db/dao/game_session_dao.dart deleted file mode 100644 index de60cfb..0000000 --- a/lib/data/db/dao/game_session_dao.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:cabo_counter/data/db/database.dart'; -import 'package:cabo_counter/data/db/tables/game_session_table.dart'; -import 'package:cabo_counter/data/dto/game_session.dart'; -import 'package:cabo_counter/data/dto/player.dart'; -import 'package:cabo_counter/data/dto/round.dart'; -import 'package:drift/drift.dart'; - -part 'game_session_dao.g.dart'; - -@DriftAccessor(tables: [GameSessionTable]) -class GameSessionDao extends DatabaseAccessor - with _$GameSessionDaoMixin { - GameSessionDao(super.db); - - /// Retrieves a game session by its ID. - Future getGameSession(String id) async { - final query = select(gameSessionTable)..where((tbl) => tbl.id.equals(id)); - final gameSessionResult = await query.getSingle(); - - List playerList = await db.playerDao.getPlayersByGameId(id); - List roundList = await db.roundsDao.getRoundsByGameId(id); - - GameSession gameSession = GameSession( - id: gameSessionResult.id, - createdAt: gameSessionResult.createdAt, - gameTitle: gameSessionResult.gameTitle, - players: playerList.map((player) => player.name).toList(), - pointLimit: gameSessionResult.pointLimit, - caboPenalty: gameSessionResult.caboPenalty, - isPointsLimitEnabled: gameSessionResult.isPointsLimitEnabled, - isGameFinished: gameSessionResult.isGameFinished, - winner: gameSessionResult.winner ?? '', - roundNumber: gameSessionResult.roundNumber, - playerScores: playerList.map((player) => player.totalScore).toList(), - roundList: roundList); - - return gameSession; - } -} diff --git a/lib/data/db/dao/rounds_dao.dart b/lib/data/db/dao/rounds_dao.dart deleted file mode 100644 index d5ab3b0..0000000 --- a/lib/data/db/dao/rounds_dao.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:cabo_counter/data/db/database.dart'; -import 'package:cabo_counter/data/db/tables/rounds_table.dart'; -import 'package:cabo_counter/data/dto/round.dart'; -import 'package:drift/drift.dart'; - -part 'rounds_dao.g.dart'; - -@DriftAccessor(tables: [RoundsTable]) -class RoundsDao extends DatabaseAccessor with _$RoundsDaoMixin { - RoundsDao(super.db); - - /// Retrieves all rounds for a specific game session by its ID. - Future> getRoundsByGameId(String gameId) async { - final query = select(roundsTable) - ..where((tbl) => tbl.gameId.equals(gameId)); - - final roundResult = await query.get(); - - final roundList = await Future.wait( - roundResult.map((row) async { - final scores = await db.roundScoresDao.getScoresByRoundId(row.roundId); - final roundScores = - await db.roundScoresDao.getScoreUpdatesByRoundId(row.roundId); - - return Round( - roundId: row.roundId, - gameId: row.gameId, - roundNum: row.roundNumber, - caboPlayerIndex: row.caboPlayerIndex, - kamikazePlayerIndex: row.kamikazePlayerIndex, - scores: scores, - scoreUpdates: roundScores, - ); - }), - ); - - return roundList; - } -} diff --git a/lib/data/db/database.dart b/lib/data/db/database.dart index 2884ced..35b3435 100644 --- a/lib/data/db/database.dart +++ b/lib/data/db/database.dart @@ -1,7 +1,7 @@ -import 'package:cabo_counter/data/db/dao/game_session_dao.dart'; -import 'package:cabo_counter/data/db/dao/player_dao.dart'; -import 'package:cabo_counter/data/db/dao/round_scores_dao.dart'; -import 'package:cabo_counter/data/db/dao/rounds_dao.dart'; +import 'package:cabo_counter/data/dao/game_session_dao.dart'; +import 'package:cabo_counter/data/dao/player_dao.dart'; +import 'package:cabo_counter/data/dao/round_scores_dao.dart'; +import 'package:cabo_counter/data/dao/rounds_dao.dart'; import 'package:cabo_counter/data/db/tables/game_session_table.dart'; import 'package:cabo_counter/data/db/tables/player_table.dart'; import 'package:cabo_counter/data/db/tables/round_scores_table.dart'; diff --git a/lib/data/dto/game_session.dart b/lib/data/dto/game_session.dart index 7b3af37..5bd0bc0 100644 --- a/lib/data/dto/game_session.dart +++ b/lib/data/dto/game_session.dart @@ -1,3 +1,4 @@ +import 'package:cabo_counter/data/dto/player.dart'; import 'package:cabo_counter/data/dto/round.dart'; import 'package:flutter/cupertino.dart'; import 'package:uuid/uuid.dart'; @@ -16,14 +17,13 @@ class GameSession extends ChangeNotifier { final String id; final DateTime createdAt; final String gameTitle; - final List players; + final List players; final int pointLimit; final int caboPenalty; final bool isPointsLimitEnabled; bool isGameFinished; String winner; int roundNumber; - List playerScores; List roundList; GameSession( @@ -37,16 +37,13 @@ class GameSession extends ChangeNotifier { this.isGameFinished = false, this.winner = '', this.roundNumber = 1, - this.playerScores = const [], - this.roundList = const []}) { - playerScores = List.filled(players.length, 0); - } + this.roundList = const []}); @override toString() { - return ('GameSession: [id: $id, createdAt: $createdAt, gameTitle: $gameTitle, ' + return 'GameSession: [id: $id, createdAt: $createdAt, gameTitle: $gameTitle, ' 'isPointsLimitEnabled: $isPointsLimitEnabled, pointLimit: $pointLimit, caboPenalty: $caboPenalty,' - ' players: $players, playerScores: $playerScores, roundList: $roundList, winner: $winner]'); + ' players: $players, roundList: $roundList, winner: $winner]'; } /// Converts the GameSession object to a JSON map. @@ -61,7 +58,6 @@ class GameSession extends ChangeNotifier { 'isGameFinished': isGameFinished, 'winner': winner, 'roundNumber': roundNumber, - 'playerScores': playerScores, 'roundList': roundList.map((e) => e.toJson()).toList() }; @@ -70,14 +66,13 @@ class GameSession extends ChangeNotifier { : id = json['id'] ?? const Uuid().v1(), createdAt = DateTime.parse(json['createdAt']), gameTitle = json['gameTitle'], - players = List.from(json['players']), + players = List.from(json['players']), pointLimit = json['pointLimit'], caboPenalty = json['caboPenalty'], isPointsLimitEnabled = json['isPointsLimitEnabled'], isGameFinished = json['isGameFinished'], winner = json['winner'], roundNumber = json['roundNumber'], - playerScores = List.from(json['playerScores']), roundList = (json['roundList'] as List).map((e) => Round.fromJson(e)).toList(); @@ -247,8 +242,8 @@ class GameSession extends ChangeNotifier { bonusPlayers = _checkHundredPointsReached(); bool limitExceeded = false; - for (int i = 0; i < playerScores.length; i++) { - if (playerScores[i] > pointLimit) { + for (int i = 0; i < players.length; i++) { + if (players[i].totalScore > pointLimit) { isGameFinished = true; limitExceeded = true; print('${players[i]} hat die 100 Punkte ueberschritten, ' @@ -271,9 +266,9 @@ class GameSession extends ChangeNotifier { /// playerScores list. void _sumPoints() { for (int i = 0; i < players.length; i++) { - playerScores[i] = 0; + players[i].totalScore = 0; for (int j = 0; j < roundList.length; j++) { - playerScores[i] += roundList[j].scoreUpdates[i]; + players[i].totalScore += roundList[j].scoreUpdates[i]; } } notifyListeners(); @@ -285,7 +280,7 @@ class GameSession extends ChangeNotifier { List _checkHundredPointsReached() { List bonusPlayers = []; for (int i = 0; i < players.length; i++) { - if (playerScores[i] == pointLimit) { + if (players[i].totalScore == pointLimit) { bonusPlayers.add(i); print('${players[i]} hat genau 100 Punkte erreicht und bekommt ' 'deswegen ${(pointLimit / 2).round()} Punkte abgezogen'); @@ -296,15 +291,23 @@ class GameSession extends ChangeNotifier { return bonusPlayers; } + List getPlayerScoresAsList() { + return players.map((player) => player.totalScore).toList(); + } + + List getPlayerNamesAsList() { + return players.map((player) => player.name).toList(); + } + /// Determines the winner of the game session. /// It iterates through the player scores and finds the player /// with the lowest score. void setWinner() { - int minScore = playerScores.reduce((a, b) => a < b ? a : b); + int minScore = getPlayerScoresAsList().reduce((a, b) => a < b ? a : b); List lowestPlayers = []; for (int i = 0; i < players.length; i++) { - if (playerScores[i] == minScore) { - lowestPlayers.add(players[i]); + if (players[i].totalScore == minScore) { + lowestPlayers.add(players[i].name); } } if (lowestPlayers.length > 1) { diff --git a/lib/data/dto/player.dart b/lib/data/dto/player.dart index 3241525..d7d1252 100644 --- a/lib/data/dto/player.dart +++ b/lib/data/dto/player.dart @@ -14,7 +14,7 @@ class Player { @override String toString() { - return '(playerId: $playerId, gameId: $gameId, name: $name, position: $position)'; + return 'Player: [playerId: $playerId, gameId: $gameId, name: $name, position: $position]'; } Map toJson() => { diff --git a/lib/presentation/views/home/active_game/active_game_view.dart b/lib/presentation/views/home/active_game/active_game_view.dart index 6c4e76f..68fefaa 100644 --- a/lib/presentation/views/home/active_game/active_game_view.dart +++ b/lib/presentation/views/home/active_game/active_game_view.dart @@ -62,7 +62,7 @@ class _ActiveGameViewState extends State { builder: (context, _) { sortedPlayerIndices = _getSortedPlayerIndices(); denseRanks = _calculateDenseRank( - gameSession.playerScores, sortedPlayerIndices); + gameSession.getPlayerScoresAsList(), sortedPlayerIndices); return CupertinoPageScaffold( navigationBar: CupertinoNavigationBar( previousPageTitle: AppLocalizations.of(context).games, @@ -117,7 +117,7 @@ class _ActiveGameViewState extends State { _getPlacementTextWidget(index), const SizedBox(width: 5), Text( - gameSession.players[playerIndex], + gameSession.players[playerIndex].name, style: const TextStyle( fontWeight: FontWeight.bold), ), @@ -127,7 +127,7 @@ class _ActiveGameViewState extends State { children: [ const SizedBox(width: 5), Text( - '${gameSession.playerScores[playerIndex]} ' + '${gameSession.getPlayerScoresAsList()[playerIndex]} ' '${AppLocalizations.of(context).points}') ], ), @@ -258,15 +258,14 @@ class _ActiveGameViewState extends State { context, CupertinoPageRoute( builder: (_) => CreateGameView( - gameTitle: - gameSession.gameTitle, - gameMode: widget.gameSession - .isPointsLimitEnabled == - true - ? GameMode.pointLimit - : GameMode.unlimited, - players: gameSession.players, - ))); + gameTitle: gameSession.gameTitle, + gameMode: widget.gameSession + .isPointsLimitEnabled == + true + ? GameMode.pointLimit + : GameMode.unlimited, + players: gameSession + .getPlayerNamesAsList()))); }, ), CupertinoListTile( @@ -374,8 +373,8 @@ class _ActiveGameViewState extends State { List.generate(gameSession.players.length, (index) => index); // Sort the indices based on the summed points playerIndices.sort((a, b) { - int scoreA = gameSession.playerScores[a]; - int scoreB = gameSession.playerScores[b]; + int scoreA = gameSession.getPlayerScoresAsList()[a]; + int scoreB = gameSession.getPlayerScoresAsList()[b]; if (scoreA != scoreB) { return scoreA.compareTo(scoreB); } @@ -515,7 +514,8 @@ class _ActiveGameViewState extends State { /// Plays the confetti animation and shows a dialog with the winner's information. Future _playFinishAnimation(BuildContext context) async { String winner = widget.gameSession.winner; - int winnerPoints = widget.gameSession.playerScores.min; + + int winnerPoints = widget.gameSession.getPlayerScoresAsList().min; int winnerAmount = winner.contains('&') ? 2 : 1; confettiController.play(); diff --git a/lib/presentation/views/home/active_game/graph_view.dart b/lib/presentation/views/home/active_game/graph_view.dart index 599326f..5190c70 100644 --- a/lib/presentation/views/home/active_game/graph_view.dart +++ b/lib/presentation/views/home/active_game/graph_view.dart @@ -94,7 +94,7 @@ class _GraphViewState extends State { List> getCumulativeScores() { final rounds = widget.gameSession.roundList; final playerCount = widget.gameSession.players.length; - final playerNames = widget.gameSession.players; + final playerNames = widget.gameSession.getPlayerNamesAsList(); List> cumulativeScores = List.generate(playerCount, (_) => []); List runningTotals = List.filled(playerCount, 0); diff --git a/lib/presentation/views/home/active_game/points_view.dart b/lib/presentation/views/home/active_game/points_view.dart index 5d9501e..2374887 100644 --- a/lib/presentation/views/home/active_game/points_view.dart +++ b/lib/presentation/views/home/active_game/points_view.dart @@ -68,7 +68,7 @@ class _PointsViewState extends State { padding: const EdgeInsets.symmetric(horizontal: 8), child: Text( - player, + player.name, style: const TextStyle( fontWeight: FontWeight.bold), overflow: TextOverflow.ellipsis, @@ -120,7 +120,7 @@ class _PointsViewState extends State { padding: const EdgeInsets.symmetric( horizontal: 8), child: Text( - player, + player.name, style: const TextStyle( fontWeight: FontWeight.bold), overflow: TextOverflow.ellipsis, @@ -236,18 +236,20 @@ class _PointsViewState extends State { fontWeight: FontWeight.bold), ), )), - ...widget.gameSession.playerScores.map( - (score) => DataCell( - Center( - child: Text( - '$score', - style: const TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold), + ...widget.gameSession + .getPlayerScoresAsList() + .map( + (score) => DataCell( + Center( + child: Text( + '$score', + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold), + ), + ), ), ), - ), - ), ], ), ], diff --git a/lib/presentation/views/home/active_game/round_view.dart b/lib/presentation/views/home/active_game/round_view.dart index 41f634d..240d815 100644 --- a/lib/presentation/views/home/active_game/round_view.dart +++ b/lib/presentation/views/home/active_game/round_view.dart @@ -146,7 +146,7 @@ class _RoundViewState extends State { .entries .map((entry) { final index = entry.key; - final name = entry.value; + final player = entry.value; return MapEntry( index, Padding( @@ -157,7 +157,7 @@ class _RoundViewState extends State { child: FittedBox( fit: BoxFit.scaleDown, child: Text( - name, + player.name, textAlign: TextAlign.center, maxLines: 1, style: const TextStyle( @@ -210,7 +210,7 @@ class _RoundViewState extends State { ])) ]), subtitle: Text( - '${widget.gameSession.playerScores[originalIndex]}' + '${widget.gameSession.getPlayerScoresAsList()[originalIndex]}' ' ${AppLocalizations.of(context).points}'), trailing: SizedBox( width: 100, @@ -329,10 +329,11 @@ class _RoundViewState extends State { /// Rotates the players list based on the previous round's winner. List _getRotatedPlayers() { final winnerIndex = _getPreviousRoundWinnerIndex(); + final playerList = widget.gameSession.getPlayerNamesAsList(); return [ - widget.gameSession.players[winnerIndex], - ...widget.gameSession.players.sublist(winnerIndex + 1), - ...widget.gameSession.players.sublist(0, winnerIndex) + playerList[winnerIndex], + ...playerList.sublist(winnerIndex + 1), + ...playerList.sublist(0, winnerIndex) ]; } @@ -358,14 +359,14 @@ class _RoundViewState extends State { message: Text(AppLocalizations.of(context).who_has_kamikaze), actions: widget.gameSession.players.asMap().entries.map((entry) { final index = entry.key; - final name = entry.value; + final player = entry.value; return CupertinoActionSheetAction( onPressed: () { _kamikazePlayerIndex = index; Navigator.pop(context, true); }, child: Text( - name, + player.name, style: TextStyle(color: CustomTheme.kamikazeColor), ), ); @@ -494,7 +495,7 @@ class _RoundViewState extends State { String _getBonusPopupMessageString( int pointLimit, int bonusPoints, List bonusPlayers) { List nameList = - bonusPlayers.map((i) => widget.gameSession.players[i]).toList(); + bonusPlayers.map((i) => widget.gameSession.players[i].name).toList(); String resultText = ''; if (nameList.length == 1) { resultText = AppLocalizations.of(context).bonus_points_message( diff --git a/lib/presentation/views/home/create_game_view.dart b/lib/presentation/views/home/create_game_view.dart index d8ec16c..5ad0c7f 100644 --- a/lib/presentation/views/home/create_game_view.dart +++ b/lib/presentation/views/home/create_game_view.dart @@ -2,6 +2,7 @@ import 'package:cabo_counter/core/constants.dart'; import 'package:cabo_counter/core/custom_theme.dart'; import 'package:cabo_counter/data/dto/game_manager.dart'; import 'package:cabo_counter/data/dto/game_session.dart'; +import 'package:cabo_counter/data/dto/player.dart'; import 'package:cabo_counter/l10n/generated/app_localizations.dart'; import 'package:cabo_counter/presentation/views/home/active_game/active_game_view.dart'; import 'package:cabo_counter/presentation/views/home/active_game/mode_selection_view.dart'; @@ -448,26 +449,38 @@ class _CreateGameViewState extends State { /// It then adds the game session to the game manager and navigates to the active game view. void _createGame() { var uuid = const Uuid(); - final String id = uuid.v1(); + final String gameId = uuid.v1(); - List players = []; + // Collect player names from the text controllers. + List playerNames = []; for (var controller in _playerNameTextControllers) { - players.add(controller.text); + playerNames.add(controller.text); + } + + // Create a list of Player objects with unique IDs and the corresponding attributes + List playerList = []; + for (int i = 0; i < playerNames.length; i++) { + playerList.add(Player( + playerId: uuid.v1(), + gameId: gameId, + name: playerNames[i], + position: i, + )); } bool isPointsLimitEnabled = gameMode == GameMode.pointLimit; GameSession gameSession = GameSession( - id: id, + id: gameId, createdAt: DateTime.now(), gameTitle: _gameTitleTextController.text, - players: players, + players: playerList, pointLimit: ConfigService.getPointLimit(), caboPenalty: ConfigService.getCaboPenalty(), isPointsLimitEnabled: isPointsLimitEnabled, isGameFinished: false); gameManager.addGameSession(gameSession); - final session = gameManager.getGameSessionById(id) ?? gameSession; + final session = gameManager.getGameSessionById(gameId) ?? gameSession; Navigator.pushAndRemoveUntil( context, diff --git a/test/data/game_session_test.dart b/test/data/game_session_test.dart index 9f054a9..cde608d 100644 --- a/test/data/game_session_test.dart +++ b/test/data/game_session_test.dart @@ -1,9 +1,25 @@ import 'package:cabo_counter/data/dto/game_session.dart'; +import 'package:cabo_counter/data/dto/player.dart'; import 'package:test/test.dart'; void main() { late GameSession session; - final testPlayers = ['Alice', 'Bob', 'Charlie']; + final testPlayers = [ + Player( + name: 'Alice', + totalScore: 0, + playerId: '0', + gameId: 'abc', + position: 0), + Player( + name: 'Bob', totalScore: 0, playerId: '1', gameId: 'abc', position: 1), + Player( + name: 'Charlie', + totalScore: 0, + playerId: '2', + gameId: 'abc', + position: 2) + ]; final testDate = DateTime(2023, 1, 1); const testTitle = 'Test Game'; @@ -23,7 +39,7 @@ void main() { test('Initialization', () { expect(session.gameTitle, testTitle); expect(session.players, testPlayers); - expect(session.playerScores, [0, 0, 0]); + expect(session.getPlayerScoresAsList(), [0, 0, 0]); expect(session.roundNumber, 1); expect(session.isGameFinished, isFalse); expect(session.winner, isEmpty); @@ -141,14 +157,14 @@ void main() { session.addRoundScoresToList(1, [10, 20, 30], [10, 20, 30], 0); session.addRoundScoresToList(2, [5, 5, 5], [5, 5, 5], 1); session.testingSumPoints(); - expect(session.playerScores, [15, 25, 35]); + expect(session.getPlayerScoresAsList(), [15, 25, 35]); }); test('_checkHundredPointsReached via updatePoints', () { session.addRoundScoresToList(1, [50, 5, 15], [50, 0, 15], 1); session.addRoundScoresToList(2, [50, 5, 15], [50, 0, 15], 1); session.updatePoints(); - expect(session.playerScores, equals([50, 0, 30])); + expect(session.getPlayerScoresAsList(), equals([50, 0, 30])); }); test('_setWinner via updatePoints', () {