From ef96a6623eb997e6db666215404e48976a6ebc5c Mon Sep 17 00:00:00 2001 From: Andrei Stoica Date: Sun, 29 Dec 2024 23:56:07 -0500 Subject: [PATCH] supergame ui --- lib/game.dart | 10 ++- lib/main.dart | 5 +- lib/super.dart | 228 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/util.dart | 28 ++++++ todo | 1 - 5 files changed, 265 insertions(+), 7 deletions(-) create mode 100644 lib/super.dart diff --git a/lib/game.dart b/lib/game.dart index c147434..3656a91 100644 --- a/lib/game.dart +++ b/lib/game.dart @@ -6,8 +6,8 @@ class TTCGame extends StatelessWidget { const TTCGame({ super.key, required this.turn, - required this.cellOnTapCallback, required this.data, + this.cellOnTapCallback, this.cellTextStyle, }); @@ -18,7 +18,7 @@ class TTCGame extends StatelessWidget { final TextStyle? cellTextStyle; /// hook into end of turn cycle; - final Function cellOnTapCallback; + final void Function(int)? cellOnTapCallback; @override Widget build(BuildContext context) { @@ -39,7 +39,7 @@ class GameHash extends StatelessWidget { const GameHash({super.key, required this.children, this.cellOnTapCallback}); final List children; - final Function? cellOnTapCallback; + final void Function(int)? cellOnTapCallback; Border _genCellBorder( int index, { @@ -93,6 +93,8 @@ class HashCell extends StatelessWidget { @override Widget build(BuildContext context) => InkWell( onTap: stateSetCallback, - child: Center(child: child), + child: IgnorePointer( + child: Center(child: child), + ), ); } diff --git a/lib/main.dart b/lib/main.dart index b895bc5..051aa2c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; -import 'clasic.dart'; +import 'package:super_tic_tac_toe/super.dart'; +import 'classic.dart'; void main() { runApp(const MyApp()); @@ -75,7 +76,7 @@ class _MyHomePageState extends State { padding: const EdgeInsets.all(10), child: type == GameType.classicTTC ? const ClassicGame() - : const Center(child: Text("Under Construction")), + : const SuperGame(), )); } } diff --git a/lib/super.dart b/lib/super.dart new file mode 100644 index 0000000..bc6e084 --- /dev/null +++ b/lib/super.dart @@ -0,0 +1,228 @@ +import 'package:flutter/material.dart'; +import 'package:super_tic_tac_toe/game.dart'; +import 'package:super_tic_tac_toe/state.dart'; +import 'package:super_tic_tac_toe/util.dart'; + +class SuperGame extends StatefulWidget { + const SuperGame({super.key}); + + @override + State createState() => _SuperGameState(); +} + +class _SuperGameState extends State { + TTCState turn = TTCState.x; + List> data = Util.emptyBoardSuper; + TTCState winner = TTCState.empty; + bool gameEnded() => winner != TTCState.empty; + + List subGameWinners = [ + TTCState.empty, + TTCState.empty, + TTCState.empty, + TTCState.empty, + TTCState.empty, + TTCState.empty, + TTCState.empty, + TTCState.empty, + TTCState.empty, + ]; + bool subGameEnded(int i) => subGameWinners[i] != TTCState.empty; + + int nextPlay = -1; + + void _swapTurn() { + switch (turn) { + case TTCState.x: + turn = TTCState.o; + break; + case TTCState.o: + turn = TTCState.x; + break; + default: + turn = TTCState.x; + } + } + + TTCState _validateSubGame(int index) { + subGameWinners[index] = Util.checkWin(data[index]); + print("validated $index"); + return subGameWinners[index]; + } + + TTCState _validateGame() { + winner = Util.checkWin(subGameWinners); + return winner; + } + + bool _checkValidChoice(List game, int index) => + game[index] == TTCState.empty; + + void Function(int) subGameCellOnTapCallback(int subGame) { + return (int i) { + if (!_checkValidChoice(data[subGame], i)) { + showDialog( + context: context, + builder: (context) => AlertDialog( + content: Text("${data[subGame][i].name.toUpperCase()}" + " already claimed " + "[${i % 3}, ${(i / 3).floor()}]"), + actions: [ + ElevatedButton( + onPressed: () => Navigator.pop(context), + child: const Text("Close")) + ], + )); + return; + } + + setState(() { + data[subGame][i] = turn; + _validateSubGame(subGame); + _validateGame(); + nextPlay = subGameEnded(i) ? -1 : i; + if (!gameEnded()) { + _swapTurn(); + } + }); + Navigator.pop(context); + }; + } + + Widget _subGameDialog(int subGame) { + return Dialog( + child: Padding( + padding: const EdgeInsets.all(10), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 5), + child: Text( + "${Util.stateText(turn)} select cell", + style: const TextStyle(fontWeight: FontWeight.bold), + ), + ), + TTCGame( + turn: turn, + cellOnTapCallback: subGameCellOnTapCallback(subGame), + data: data[subGame], + ), + Padding( + padding: const EdgeInsets.only(top: 5), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + ElevatedButton( + onPressed: () { + Navigator.pop(context); + }, + child: const Text("Close")) + ], + ), + ), + ], + ), + ), + ); + } + + void endedSubGameNotification(int index) { + ScaffoldMessenger.of(context) + ..clearSnackBars() + ..showSnackBar( + SnackBar( + content: Text("${Util.stateText(subGameWinners[index])}" + " already won the game at " + "[${Util.cellAddress(index)}]"), + ), + ); + } + + void _showSubGameDialog(int i) { + if (nextPlay == i || nextPlay == -1) { + showDialog( + context: context, + builder: (context) => _subGameDialog(i), + ); + } else { + ScaffoldMessenger.of(context) + ..clearSnackBars() + ..showSnackBar( + SnackBar( + content: Text( + "You can only play at [${(nextPlay % 3) + 1}," + "${(nextPlay / 3).floor() + 1}]", + ), + ), + ); + } + } + + void _gameEndedReminder() { + ScaffoldMessenger.of(context) + ..clearSnackBars() + ..showSnackBar( + SnackBar( + content: Text("${Util.stateText(winner)} already won the game"), + ), + ); + } + + void _cellOnTapCallback(int index) { + if (subGameEnded(index)) { + endedSubGameNotification(index); + return; + } else if (gameEnded()) { + _gameEndedReminder(); + return; + } + _showSubGameDialog(index); + } + + @override + Widget build(BuildContext context) { + Iterable.generate(subGameWinners.length) + .map((i) => subGameEnded(i)) + .forEach(print); + + return Column( + children: [ + const Spacer(flex: 5), + Center( + child: Text( + gameEnded() + ? "${Util.stateText(winner)} Wins" + : "${Util.stateText(turn)}'s Turn", + style: const TextStyle(fontSize: 25), + ), + ), + const Spacer(flex: 1), + GameHash( + cellOnTapCallback: _cellOnTapCallback, + children: Iterable.generate(data.length) + .map( + (i) => DecoratedBox( + decoration: BoxDecoration( + color: nextPlay == i ? Colors.lightGreenAccent : null, + ), + child: Padding( + padding: const EdgeInsets.all(15), + child: !subGameEnded(i) + ? TTCGame( + turn: turn, + data: data[i], + ) + : Text( + subGameWinners[i].name.toUpperCase(), + style: const TextStyle(fontSize: 40), + ), + ), + ), + ) + .toList()), + const Spacer(flex: 5), + ], + ); + } +} diff --git a/lib/util.dart b/lib/util.dart index 62ed3be..b0f9fff 100644 --- a/lib/util.dart +++ b/lib/util.dart @@ -1,6 +1,34 @@ import 'state.dart'; class Util { + static List get emptyBoardClassic => [ + TTCState.empty, + TTCState.empty, + TTCState.empty, + TTCState.empty, + TTCState.empty, + TTCState.empty, + TTCState.empty, + TTCState.empty, + TTCState.empty, + ]; + + static List> get emptyBoardSuper => [ + emptyBoardClassic, + emptyBoardClassic, + emptyBoardClassic, + emptyBoardClassic, + emptyBoardClassic, + emptyBoardClassic, + emptyBoardClassic, + emptyBoardClassic, + emptyBoardClassic, + ]; + + static String stateText(TTCState state) => state.name.toUpperCase(); + static String cellAddress(int index) => + "${index % 3}, ${(index / 3).floor()}"; + static Iterable getRow(int index, List data) => data.getRange(index, index + 3); static Iterable getCol(int index, List data) => [ diff --git a/todo b/todo index 208be91..bb8c266 100644 --- a/todo +++ b/todo @@ -1,2 +1 @@ -- ui for super game - firebase for multiplayer game