Implemented round logic

This commit is contained in:
Felix Kirchner
2025-03-03 00:53:31 +01:00
parent 943943a335
commit ac6f9454ad
2 changed files with 310 additions and 138 deletions

View File

@@ -21,17 +21,28 @@ class GameSession {
@override @override
String toString() { String toString() {
print('GameSession: [gameTitle: $gameTitle, ' return ('GameSession: [gameTitle: $gameTitle, '
'players: $players, winner: $winner, ' 'players: $players, winner: $winner, '
'round: $round, gameMode: $gameMode, ' 'round: $round, gameMode: $gameMode, '
'playerScores: $playerScores]'); 'playerScores: $playerScores]');
return super.toString(); }
void expandPlayerScoreLists() {
for (int i = 0; i < playerScores.length; i++) {
playerScores[i].add(0);
}
}
void addRoundScoresToScoreList(List<int> roundScores, int roundNumber) {
for (int i = 0; i < roundScores.length; i++) {
playerScores[i][roundNumber] = (roundScores[i]);
}
} }
void sumPoints() { void sumPoints() {
for (int i = 0; i < playerScores.length; i++) { for (int i = 0; i < playerScores.length; i++) {
playerScores[i][0] = 0; playerScores[i][0] = 0;
for (int j = 1; j < playerScores[i].length; i++) { for (int j = 1; j < playerScores[i].length; j++) {
playerScores[i][0] += playerScores[i][j]; playerScores[i][0] += playerScores[i][j];
} }
} }

View File

@@ -15,24 +15,32 @@ class RoundView extends StatefulWidget {
} }
class _RoundViewState extends State<RoundView> { class _RoundViewState extends State<RoundView> {
/// The current game session.
late GameSession gameSession = widget.gameSession;
/// Index of the player who said CABO.
int _caboPlayerIndex = 0;
/// List of booleans that represent whether the kamikaze checkbox is checked for a player on that index.
late final List<bool> _isKamikazeChecked = late final List<bool> _isKamikazeChecked =
List.filled(widget.gameSession.players.length, false); List.filled(widget.gameSession.players.length, false);
/// List of text controllers for the point text fields.
late final List<TextEditingController> _pointControllers = List.generate( late final List<TextEditingController> _pointControllers = List.generate(
widget.gameSession.players.length, widget.gameSession.players.length,
(index) => TextEditingController(), (index) => TextEditingController(),
); );
int _caboPlayerIndex = 0;
late GameSession gameSession = widget.gameSession; /// List of focus nodes for the point text fields.
late final List<FocusNode> _focusNodes = List.generate( late final List<FocusNode> _focusNodes = List.generate(
widget.gameSession.players.length, widget.gameSession.players.length,
(index) => FocusNode(), (index) => FocusNode(),
); // Neue FocusNodes-Liste );
@override @override
void initState() { void initState() {
super.initState();
print('Runde ${widget.roundNumber} geöffnet'); print('Runde ${widget.roundNumber} geöffnet');
super.initState();
} }
@override @override
@@ -42,149 +50,291 @@ class _RoundViewState extends State<RoundView> {
navigationBar: const CupertinoNavigationBar( navigationBar: const CupertinoNavigationBar(
transitionBetweenRoutes: true, transitionBetweenRoutes: true,
middle: Text('Ergebnisse'), middle: Text('Ergebnisse'),
previousPageTitle: 'Zurück', previousPageTitle: 'Übersicht',
), ),
child: SafeArea( child: Stack(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Padding( SafeArea(
padding: const EdgeInsets.fromLTRB(0, 40, 0, 50), child: Column(
child: Text( crossAxisAlignment: CrossAxisAlignment.center,
'Runde ${widget.roundNumber}', children: [
style: Styles.roundTitle, Padding(
), padding: const EdgeInsets.fromLTRB(0, 40, 0, 50),
), child: Text(
Padding( 'Runde ${widget.roundNumber}',
padding: const EdgeInsets.fromLTRB(0, 0, 0, 10), style: Styles.roundTitle,
child: Text('Wer hat CABO gesagt?', ),
style: TextStyle(fontWeight: FontWeight.bold)), ),
), Padding(
Padding( padding: const EdgeInsets.fromLTRB(0, 0, 0, 10),
padding: EdgeInsets.symmetric( child: Text('Wer hat CABO gesagt?',
horizontal: gameSession.players.length > 3 ? 5 : 20, style: TextStyle(fontWeight: FontWeight.bold)),
vertical: 10), ),
child: SizedBox( Padding(
height: 40, padding: EdgeInsets.symmetric(
child: CupertinoSegmentedControl<int>( horizontal: gameSession.players.length > 3 ? 5 : 20,
groupValue: _caboPlayerIndex, vertical: 10),
children: Map<int, Widget>.fromIterable( child: SizedBox(
widget.gameSession.players.asMap().keys, height: 40,
value: (index) => Padding( child: CupertinoSegmentedControl<int>(
padding: EdgeInsets.symmetric( groupValue: _caboPlayerIndex,
horizontal: gameSession.players.length > 3 ? 7 : 15, children: Map<int, Widget>.fromIterable(
), widget.gameSession.players.asMap().keys,
child: Text( value: (index) => Padding(
widget.gameSession.players[index], padding: EdgeInsets.symmetric(
style: TextStyle( horizontal: gameSession.players.length > 3 ? 7 : 15,
fontSize: gameSession.players.length > 3 ? 14 : 16), ),
child: Text(
widget.gameSession.players[index],
style: TextStyle(
fontSize:
gameSession.players.length > 3 ? 14 : 16),
),
),
), ),
onValueChanged: (int value) {
setState(() {
_caboPlayerIndex = value;
});
},
), ),
), ),
onValueChanged: (int value) {
setState(() {
_caboPlayerIndex = value;
});
},
), ),
), Padding(
), padding:
Padding( EdgeInsets.symmetric(vertical: 0.0, horizontal: 20.0),
padding: EdgeInsets.symmetric(vertical: 0.0, horizontal: 20.0), child: CupertinoListTile(
child: CupertinoListTile( title: Text('Spieler:in'),
title: Text('Spieler:in'), trailing: Row(
trailing: Row( children: [
children: [ SizedBox(
SizedBox( width: 100,
width: 100, child: Center(child: Text('Punkte')),
child: Center(child: Text('Punkte')), ),
SizedBox(width: 20),
SizedBox(
width: 70,
child: Center(child: Text('Kamikaze')),
),
],
), ),
SizedBox(width: 20), ),
SizedBox(
width: 70,
child: Center(child: Text('Kamikaze')),
),
],
), ),
), Expanded(
), child: ListView.builder(
Expanded( shrinkWrap: true,
child: ListView.builder( itemCount: widget.gameSession.players.length,
shrinkWrap: true, itemBuilder: (BuildContext context, int index) {
itemCount: widget.gameSession.players.length, return Padding(
itemBuilder: (BuildContext context, int index) { padding: EdgeInsets.symmetric(
return Padding( vertical: 10.0, horizontal: 20.0),
padding: child: ClipRRect(
EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0), borderRadius:
child: ClipRRect( BorderRadius.circular(12), // Radius der Ecken
borderRadius: child: CupertinoListTile(
BorderRadius.circular(12), // Radius der Ecken backgroundColor: CupertinoColors.secondaryLabel,
child: CupertinoListTile( title: Row(
backgroundColor: CupertinoColors.secondaryLabel, children: [
title: Row( Text(widget.gameSession.players[index]),
children: [ ],
Text(widget.gameSession.players[index]), ),
], subtitle: Text(
), '${widget.gameSession.playerScores[index][0]} Punkte',
subtitle: Text( ),
'${widget.gameSession.playerScores[index][widget.roundNumber - 1]} Punkte', trailing: Row(
), children: [
trailing: Row( SizedBox(
children: [ width: 100,
SizedBox( child: CupertinoTextField(
width: 100, maxLength: 3,
child: CupertinoTextField( focusNode: _focusNodes[index],
maxLength: 3, keyboardType:
focusNode: _focusNodes[index], TextInputType.numberWithOptions(
//keyboardType: signed: true,
// TextInputType.numberWithOptions( decimal: false,
// signed: false, decimal: false), ),
keyboardType: inputFormatters: [
TextInputType.numberWithOptions( FilteringTextInputFormatter
signed: true, .digitsOnly,
decimal: false, ],
), textInputAction: index ==
inputFormatters: [ widget.gameSession.players
FilteringTextInputFormatter.digitsOnly, .length -
], 1
textInputAction: index == ? TextInputAction.done
widget.gameSession.players.length - : TextInputAction.next,
1 controller: _pointControllers[index],
? TextInputAction.done placeholder: 'Punkte',
: TextInputAction.next, textAlign: TextAlign.center,
controller: _pointControllers[index], // 👇 Action-Typ explizit setzen
placeholder: 'Punkte',
textAlign: TextAlign.center,
// 👇 Action-Typ explizit setzen
onSubmitted: (_) => _nextField(index), onSubmitted: (_) => _nextField(index),
), ),
), ),
SizedBox(width: 20), SizedBox(width: 20),
SizedBox( SizedBox(
width: 50, width: 50,
child: CupertinoCheckbox( child: CupertinoCheckbox(
activeColor: Styles.primaryColor, activeColor: Styles.primaryColor,
tristate: false, tristate: false,
value: _isKamikazeChecked[index], value: _isKamikazeChecked[index],
onChanged: (bool? value) { onChanged: (bool? value) {
print('value: $value'); setState(() {
setState(() { _isKamikazeChecked[index] = value!;
_isKamikazeChecked[index] = value!; });
print( },
'Kamikaze checked: ${_isKamikazeChecked[index]}'); ),
}); ),
}, ],
), ))));
), },
], )),
)))); ],
},
)), )),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
height: 80, // Höhe der Bar
decoration: const BoxDecoration(
color: CupertinoColors.darkBackgroundGray,
),
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
CupertinoButton(
child: const Text('Fertig'),
onPressed: () => Navigator.pop(context),
),
CupertinoButton(
child: const Text('Nächste Runde'),
onPressed: () => {
print('===================================='),
print('Runde ${widget.roundNumber} beendet'),
if (widget.roundNumber >=
widget.gameSession.playerScores[0].length)
{gameSession.expandPlayerScoreLists()},
_checkForWinner(),
widget.gameSession.sumPoints(),
Navigator.pushReplacement(context,
CupertinoPageRoute(builder: (context) {
return RoundView(
gameSession: widget.gameSession,
roundNumber: widget.roundNumber + 1);
})),
}),
],
)),
),
),
], ],
))); ));
} }
/// Checks the scores of the current round and assigns points to the players.
/// There are two possible outcomes of a round:
///
/// **Case 1**<br>
/// The player who said CABO has the lowest score. They receive 0 points.
/// Every other player gets their round score.
///
/// **Case 2**<br>
/// The player who said CABO does not have the lowest score.
/// They receive 5 extra points added to their round score.
/// Every player with the lowest score gets 0 points.
/// Every other player gets their round score.
void _checkForWinner() {
/// List of the scores of the current round
List<int> roundScores = [];
for (TextEditingController c in _pointControllers) {
roundScores.add(int.parse(c.text));
}
print('Spieler: ${gameSession.players}');
print('Punkte: $roundScores');
print('${gameSession.players[_caboPlayerIndex]} hat CABO gesagt');
print(
'${gameSession.players[_caboPlayerIndex]} hat ${roundScores[_caboPlayerIndex]} Punkte');
/// List of the index of the player(s) with the lowest score
List<int> lowestScoreIndex = _getLowestScoreIndex(roundScores);
// Spieler der CABO gesagt hat, hat am wenigsten Punkte
if (lowestScoreIndex.contains(_caboPlayerIndex)) {
print(
'${widget.gameSession.players[_caboPlayerIndex]} hat CABO gesagt und bekommt 0 Punkte');
print('Alle anderen Spieler bekommen ihre Punkte');
///
_assignPoints([_caboPlayerIndex], -1, roundScores);
} else {
// Ein anderer Spieler hat weniger Punkte
print(
'${widget.gameSession.players[_caboPlayerIndex]} hat CABO gesagt, jedoch nicht die wenigsten Punkte.');
print('Folgende:r Spieler haben die wenigsten Punkte:');
for (int i in lowestScoreIndex) {
print('${widget.gameSession.players[i]}: ${roundScores[i]} Punkte');
}
_assignPoints(lowestScoreIndex, _caboPlayerIndex, roundScores);
}
}
/// Assigns points to the players based on the scores of the current round.
/// [winnerIndex] is the index of the player(s) who receive 0 points
/// [loserIndex] is the index of the player who receives 5 extra points
/// [roundScores] is the raw list of the scores of all players in the current round.
void _assignPoints(
List<int> winnnerIndex, int loserIndex, List<int> roundScores) {
print('Punkte der Spieler');
for (int i = 0; i < roundScores.length; i++) {
print('${widget.gameSession.players[i]}: ${roundScores[i]}');
}
for (int i in winnnerIndex) {
print('${widget.gameSession.players[i]} bekommt 0 Punkte');
roundScores[i] = 0;
}
if (loserIndex != -1) {
print('${widget.gameSession.players[loserIndex]} bekommt 5 Fehlerpunkte');
roundScores[loserIndex] += 5;
}
print('Aktualisierte Punkte');
for (int i = 0; i < roundScores.length; i++) {
print('${widget.gameSession.players[i]}: ${roundScores[i]}');
}
gameSession.addRoundScoresToScoreList(roundScores, widget.roundNumber);
}
/// Returns the index of the player with the lowest score. If there are
/// multiple players with the same lowest score, all of them are returned.
/// [roundScores] is a list of the scores of all players in the current round.
List<int> _getLowestScoreIndex(List<int> roundScores) {
int lowestScore = roundScores[0];
List<int> lowestScoreIndex = [0];
print(
'Niedrigster Score: ${gameSession.players[lowestScoreIndex[0]]} ($lowestScore Punkte)');
for (int i = 1; i < roundScores.length; i++) {
if (roundScores[i] < lowestScore) {
print(
'Neuer niedrigster Score: ${gameSession.players[i]} (${roundScores[i]} Punkte)');
lowestScore = roundScores[i];
lowestScoreIndex = [i];
} else if (roundScores[i] == lowestScore) {
print(
'${gameSession.players[i]} hat ebenfalls am wenigsten Punkte (${roundScores[i]} Punkte)');
lowestScoreIndex.add(i);
}
}
print('Folgende Spieler haben die niedrigsten Punte:');
for (int i in lowestScoreIndex) {
print('${widget.gameSession.players[i]} (${roundScores[i]} Punkte)');
}
return lowestScoreIndex;
}
/// Focuses the next text field in the list of text fields.
/// [index] is the index of the current text field.
void _nextField(int index) { void _nextField(int index) {
if (index < widget.gameSession.players.length - 1) { if (index < widget.gameSession.players.length - 1) {
FocusScope.of(context).requestFocus(_focusNodes[index + 1]); FocusScope.of(context).requestFocus(_focusNodes[index + 1]);
@@ -192,4 +342,15 @@ class _RoundViewState extends State<RoundView> {
_focusNodes[index].unfocus(); _focusNodes[index].unfocus();
} }
} }
@override
void dispose() {
for (final controller in _pointControllers) {
controller.dispose();
}
for (final focusNode in _focusNodes) {
focusNode.dispose();
}
super.dispose();
}
} }