NEW: MIH MineSweeper Package pt1
This commit is contained in:
Binary file not shown.
@@ -1,10 +1,10 @@
|
||||
@font-face {
|
||||
font-family: 'Mih_Icons';
|
||||
src: url('fonts/Mih_Icons.eot?blbuxz');
|
||||
src: url('fonts/Mih_Icons.eot?blbuxz#iefix') format('embedded-opentype'),
|
||||
url('fonts/Mih_Icons.ttf?blbuxz') format('truetype'),
|
||||
url('fonts/Mih_Icons.woff?blbuxz') format('woff'),
|
||||
url('fonts/Mih_Icons.svg?blbuxz#Mih_Icons') format('svg');
|
||||
font-family: 'icomoon';
|
||||
src: url('fonts/icomoon.eot?8flwgj');
|
||||
src: url('fonts/icomoon.eot?8flwgj#iefix') format('embedded-opentype'),
|
||||
url('fonts/icomoon.ttf?8flwgj') format('truetype'),
|
||||
url('fonts/icomoon.woff?8flwgj') format('woff'),
|
||||
url('fonts/icomoon.svg?8flwgj#icomoon') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
@@ -13,7 +13,7 @@
|
||||
[class^="icon-"],
|
||||
[class*=" icon-"] {
|
||||
/* use !important to prevent issues with browser extensions that change fonts */
|
||||
font-family: 'Mih_Icons' !important;
|
||||
font-family: 'icomoon' !important;
|
||||
/* speak: never; */
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
@@ -26,70 +26,74 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-mzansi_directory:before {
|
||||
.icon-mine_sweeper:before {
|
||||
content: "\e900";
|
||||
}
|
||||
|
||||
.icon-personal_profile:before {
|
||||
.icon-mzansi_directory:before {
|
||||
content: "\e901";
|
||||
}
|
||||
|
||||
.icon-about_mih:before {
|
||||
.icon-personal_profile:before {
|
||||
content: "\e902";
|
||||
}
|
||||
|
||||
.icon-access_control:before {
|
||||
.icon-about_mih:before {
|
||||
content: "\e903";
|
||||
}
|
||||
|
||||
.icon-business_profile:before {
|
||||
.icon-access_control:before {
|
||||
content: "\e904";
|
||||
}
|
||||
|
||||
.icon-business_setup:before {
|
||||
.icon-business_profile:before {
|
||||
content: "\e905";
|
||||
}
|
||||
|
||||
.icon-i_dont_know:before {
|
||||
.icon-business_setup:before {
|
||||
content: "\e906";
|
||||
}
|
||||
|
||||
.icon-mih_logo:before {
|
||||
.icon-calculator:before {
|
||||
content: "\e907";
|
||||
}
|
||||
|
||||
.icon-mih_ring:before {
|
||||
.icon-calendar:before {
|
||||
content: "\e908";
|
||||
}
|
||||
|
||||
.icon-mzansi_ai:before {
|
||||
.icon-i_dont_know:before {
|
||||
content: "\e909";
|
||||
}
|
||||
|
||||
.icon-mzansi_wallet:before {
|
||||
.icon-mih_logo:before {
|
||||
content: "\e90a";
|
||||
}
|
||||
|
||||
.icon-notifications:before {
|
||||
.icon-mih_ring:before {
|
||||
content: "\e90b";
|
||||
}
|
||||
|
||||
.icon-patient_manager:before {
|
||||
.icon-mzansi_ai:before {
|
||||
content: "\e90c";
|
||||
}
|
||||
|
||||
.icon-patient_profile:before {
|
||||
.icon-mzansi_wallet:before {
|
||||
content: "\e90d";
|
||||
}
|
||||
|
||||
.icon-profile_setup:before {
|
||||
.icon-notifications:before {
|
||||
content: "\e90e";
|
||||
}
|
||||
|
||||
.icon-calculator:before {
|
||||
content: "\e940";
|
||||
.icon-patient_manager:before {
|
||||
content: "\e90f";
|
||||
}
|
||||
|
||||
.icon-calendar:before {
|
||||
content: "\e953";
|
||||
.icon-patient_profile:before {
|
||||
content: "\e910";
|
||||
}
|
||||
|
||||
.icon-profile_setup:before {
|
||||
content: "\e911";
|
||||
}
|
||||
@@ -11,54 +11,57 @@ class MihIcons {
|
||||
// IconData constants based on your style.css file
|
||||
// Note: We convert the hex code from CSS (\eXXX) to an integer (0xeXXX)
|
||||
|
||||
static const IconData mzansiDirectory =
|
||||
static const IconData mineSweeper =
|
||||
IconData(0xe900, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
|
||||
|
||||
static const IconData personalProfile =
|
||||
static const IconData mzansiDirectory =
|
||||
IconData(0xe901, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
|
||||
|
||||
static const IconData aboutMih =
|
||||
static const IconData personalProfile =
|
||||
IconData(0xe902, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
|
||||
|
||||
static const IconData accessControl =
|
||||
static const IconData aboutMih =
|
||||
IconData(0xe903, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
|
||||
|
||||
static const IconData businessProfile =
|
||||
static const IconData accessControl =
|
||||
IconData(0xe904, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
|
||||
|
||||
static const IconData businessSetup =
|
||||
static const IconData businessProfile =
|
||||
IconData(0xe905, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
|
||||
|
||||
static const IconData iDontKnow =
|
||||
static const IconData businessSetup =
|
||||
IconData(0xe906, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
|
||||
|
||||
static const IconData mihLogo =
|
||||
static const IconData calculator =
|
||||
IconData(0xe907, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
|
||||
|
||||
static const IconData mihRing =
|
||||
static const IconData calendar =
|
||||
IconData(0xe908, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
|
||||
|
||||
static const IconData mzansiAi =
|
||||
static const IconData iDontKnow =
|
||||
IconData(0xe909, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
|
||||
|
||||
static const IconData mzansiWallet =
|
||||
static const IconData mihLogo =
|
||||
IconData(0xe90a, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
|
||||
|
||||
static const IconData notifications =
|
||||
static const IconData mihRing =
|
||||
IconData(0xe90b, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
|
||||
|
||||
static const IconData patientManager =
|
||||
static const IconData mzansiAi =
|
||||
IconData(0xe90c, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
|
||||
|
||||
static const IconData patientProfile =
|
||||
static const IconData mzansiWallet =
|
||||
IconData(0xe90d, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
|
||||
|
||||
static const IconData profileSetup =
|
||||
static const IconData notifications =
|
||||
IconData(0xe90e, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
|
||||
|
||||
static const IconData calculator =
|
||||
IconData(0xe940, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
|
||||
static const IconData patientManager =
|
||||
IconData(0xe90f, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
|
||||
|
||||
static const IconData calendar =
|
||||
IconData(0xe953, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
|
||||
static const IconData patientProfile =
|
||||
IconData(0xe910, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
|
||||
|
||||
static const IconData profileSetup =
|
||||
IconData(0xe911, fontFamily: _mihFontFam, fontPackage: _mihFontPkg);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
class BoardSquare {
|
||||
bool hasBomb;
|
||||
int bombsAround;
|
||||
bool isOpened;
|
||||
bool isFlagged;
|
||||
|
||||
BoardSquare({
|
||||
this.hasBomb = false,
|
||||
this.bombsAround = 0,
|
||||
this.isOpened = false,
|
||||
this.isFlagged = false,
|
||||
});
|
||||
}
|
||||
111
Frontend/lib/mih_packages/mine_sweeper/components/mine_tile.dart
Normal file
111
Frontend/lib/mih_packages/mine_sweeper/components/mine_tile.dart
Normal file
@@ -0,0 +1,111 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:mzansi_innovation_hub/main.dart';
|
||||
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_button.dart';
|
||||
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
|
||||
import 'package:mzansi_innovation_hub/mih_packages/mine_sweeper/components/board_square.dart';
|
||||
|
||||
class MineTile extends StatelessWidget {
|
||||
final BoardSquare square;
|
||||
final VoidCallback onTap;
|
||||
final VoidCallback onLongPress;
|
||||
|
||||
const MineTile({
|
||||
super.key,
|
||||
required this.square,
|
||||
required this.onTap,
|
||||
required this.onLongPress,
|
||||
});
|
||||
|
||||
Widget _getTileContent(BuildContext context) {
|
||||
if (square.isFlagged) {
|
||||
return Icon(
|
||||
Icons.flag,
|
||||
color: MihColors.getRedColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode != "Dark",
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (square.isOpened) {
|
||||
if (square.hasBomb) {
|
||||
return const Icon(FontAwesomeIcons.bomb, color: Colors.black);
|
||||
} else if (square.bombsAround > 0) {
|
||||
// Display bomb count
|
||||
return Center(
|
||||
child: Text(
|
||||
'${square.bombsAround}',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: _getTileColor(square.bombsAround, context),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// Opened, but no bomb count (empty square)
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
}
|
||||
|
||||
// Default: Unopened tile
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
Color _getTileColor(int bombsAround, BuildContext context) {
|
||||
// Choose colors based on standard Minesweeper appearance
|
||||
switch (bombsAround) {
|
||||
case 1:
|
||||
return MihColors.getBluishPurpleColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode != "Dark",
|
||||
);
|
||||
// return Colors.blue;
|
||||
case 2:
|
||||
return MihColors.getGreenColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode != "Dark",
|
||||
);
|
||||
// return Colors.green;
|
||||
case 3:
|
||||
return MihColors.getRedColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode != "Dark",
|
||||
);
|
||||
// return Colors.red;
|
||||
case 4:
|
||||
return MihColors.getPurpleColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode != "Dark",
|
||||
);
|
||||
// return Colors.purple;
|
||||
case 5:
|
||||
return MihColors.getOrangeColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode != "Dark",
|
||||
);
|
||||
// return Colors.brown;
|
||||
default:
|
||||
// return MihColors.getBluishPurpleColor(
|
||||
// MzansiInnovationHub.of(context)!.theme.mode == "Dark",
|
||||
// );
|
||||
return Colors.black;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(1.0),
|
||||
child: MihButton(
|
||||
onPressed: onTap,
|
||||
onLongPressed: onLongPress,
|
||||
buttonColor: square.isOpened
|
||||
? MihColors.getGreyColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode == "Dark",
|
||||
)
|
||||
: MihColors.getSecondaryColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode == "Dark",
|
||||
),
|
||||
width: 50,
|
||||
height: 50,
|
||||
borderRadius: 3,
|
||||
child: _getTileContent(context),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package.dart';
|
||||
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_action.dart';
|
||||
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_tools.dart';
|
||||
import 'package:mzansi_innovation_hub/mih_components/mih_providers/mih_mine_sweeper_provider.dart';
|
||||
import 'package:mzansi_innovation_hub/mih_packages/mine_sweeper/package_tools/mine_sweeper_game.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class MihMineSweeper extends StatefulWidget {
|
||||
const MihMineSweeper({super.key});
|
||||
@@ -10,6 +18,54 @@ class MihMineSweeper extends StatefulWidget {
|
||||
class _MihMineSweeperState extends State<MihMineSweeper> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Placeholder();
|
||||
return MihPackage(
|
||||
appActionButton: getAction(),
|
||||
appTools: getTools(),
|
||||
appToolTitles: getToolTitle(),
|
||||
appBody: getToolBody(),
|
||||
selectedbodyIndex: context.watch<MihMineSweeperProvider>().toolIndex,
|
||||
onIndexChange: (newIndex) {
|
||||
context.read<MihMineSweeperProvider>().setToolIndex(newIndex);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
MihPackageAction getAction() {
|
||||
return MihPackageAction(
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
iconSize: 35,
|
||||
onTap: () {
|
||||
context.goNamed(
|
||||
'mihHome',
|
||||
extra: true,
|
||||
);
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
MihPackageTools getTools() {
|
||||
Map<Widget, void Function()?> temp = {};
|
||||
temp[const Icon(FontAwesomeIcons.bomb)] = () {
|
||||
context.read<MihMineSweeperProvider>().setToolIndex(0);
|
||||
};
|
||||
return MihPackageTools(
|
||||
tools: temp,
|
||||
selcetedIndex: context.watch<MihMineSweeperProvider>().toolIndex,
|
||||
);
|
||||
}
|
||||
|
||||
List<String> getToolTitle() {
|
||||
List<String> toolTitles = [
|
||||
"MineSweeper",
|
||||
];
|
||||
return toolTitles;
|
||||
}
|
||||
|
||||
List<Widget> getToolBody() {
|
||||
List<Widget> toolBodies = [
|
||||
const MineSweeperGame(),
|
||||
];
|
||||
return toolBodies;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:mzansi_innovation_hub/main.dart';
|
||||
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_icons.dart';
|
||||
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_tile.dart';
|
||||
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
|
||||
|
||||
class MihMineSweeperTile extends StatefulWidget {
|
||||
final bool personalSelected;
|
||||
final double packageSize;
|
||||
const MihMineSweeperTile({
|
||||
super.key,
|
||||
required this.personalSelected,
|
||||
required this.packageSize,
|
||||
});
|
||||
|
||||
@override
|
||||
State<MihMineSweeperTile> createState() => _MihMineSweeperTileState();
|
||||
}
|
||||
|
||||
class _MihMineSweeperTileState extends State<MihMineSweeperTile> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MihPackageTile(
|
||||
onTap: () {
|
||||
context.goNamed(
|
||||
"mihMineSweeper",
|
||||
);
|
||||
},
|
||||
appName: "MineSweeper",
|
||||
appIcon: Icon(
|
||||
MihIcons.mineSweeper,
|
||||
color: MihColors.getSecondaryColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||
// size: widget.packageSize,
|
||||
),
|
||||
iconSize: widget.packageSize,
|
||||
primaryColor: MihColors.getSecondaryColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||
secondaryColor: MihColors.getPrimaryColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,596 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:mzansi_innovation_hub/main.dart';
|
||||
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_button.dart';
|
||||
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_dropdwn_field.dart';
|
||||
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_floating_menu.dart';
|
||||
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_form.dart';
|
||||
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_icons.dart';
|
||||
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_alert.dart';
|
||||
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_window.dart';
|
||||
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_single_child_scroll.dart';
|
||||
import 'package:mzansi_innovation_hub/mih_components/mih_providers/mih_mine_sweeper_provider.dart';
|
||||
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
|
||||
import 'package:mzansi_innovation_hub/mih_packages/mine_sweeper/components/board_square.dart';
|
||||
import 'package:mzansi_innovation_hub/mih_packages/mine_sweeper/components/mine_tile.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class MineSweeperGame extends StatefulWidget {
|
||||
const MineSweeperGame({super.key});
|
||||
|
||||
@override
|
||||
State<MineSweeperGame> createState() => _MineSweeperGameState();
|
||||
}
|
||||
|
||||
class _MineSweeperGameState extends State<MineSweeperGame> {
|
||||
TextEditingController modeController = TextEditingController();
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
List<List<BoardSquare>> board = [];
|
||||
bool isGameOver = false;
|
||||
bool isGameWon = false;
|
||||
int squaresLeft = -1;
|
||||
bool _isFirstLoad = true;
|
||||
|
||||
void showStartGameWindow(MihMineSweeperProvider mihMineSweeperProvider) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return MihPackageWindow(
|
||||
fullscreen: false,
|
||||
windowTitle: "New Game Settings",
|
||||
onWindowTapClose: () {
|
||||
context.pop();
|
||||
},
|
||||
windowBody: Column(
|
||||
children: [
|
||||
MihForm(
|
||||
formKey: _formKey,
|
||||
formFields: [
|
||||
MihDropdownField(
|
||||
controller: modeController,
|
||||
hintText: "Difficulty",
|
||||
dropdownOptions: ["Easy", "Normal", "Hard"],
|
||||
requiredText: true,
|
||||
editable: true,
|
||||
enableSearch: false,
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
Center(
|
||||
child: MihButton(
|
||||
onPressed: () {
|
||||
setState(
|
||||
() => initializeBoard(mihMineSweeperProvider));
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
buttonColor: MihColors.getGreenColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode ==
|
||||
"Dark"),
|
||||
width: 300,
|
||||
child: Text(
|
||||
"Start Game",
|
||||
style: TextStyle(
|
||||
color: MihColors.getPrimaryColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode ==
|
||||
"Dark"),
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// --- GAME INITIALIZATION LOGIC ---
|
||||
void initializeBoard(MihMineSweeperProvider mihMineSweeperProvider) {
|
||||
// 1. Create a board of empty squares
|
||||
board = List.generate(
|
||||
mihMineSweeperProvider.rowCount,
|
||||
(i) => List.generate(
|
||||
mihMineSweeperProvider.columnCount,
|
||||
(j) => BoardSquare(),
|
||||
),
|
||||
);
|
||||
// 2. Place bombs randomly
|
||||
placeBombs(mihMineSweeperProvider);
|
||||
// 3. Calculate the number of bombs around each non-mine square
|
||||
calculateBombsAround(mihMineSweeperProvider);
|
||||
// Reset state variables
|
||||
squaresLeft =
|
||||
mihMineSweeperProvider.rowCount * mihMineSweeperProvider.columnCount;
|
||||
isGameOver = false;
|
||||
isGameWon = false;
|
||||
// You'd typically add a call to setState here, but it's in initState.
|
||||
}
|
||||
|
||||
void placeBombs(MihMineSweeperProvider mihMineSweeperProvider) {
|
||||
final Random random = Random();
|
||||
int bombsPlaced = 0;
|
||||
|
||||
while (bombsPlaced < mihMineSweeperProvider.totalMines) {
|
||||
int r = random.nextInt(mihMineSweeperProvider.rowCount);
|
||||
int c = random.nextInt(mihMineSweeperProvider.columnCount);
|
||||
|
||||
if (!board[r][c].hasBomb) {
|
||||
board[r][c].hasBomb = true;
|
||||
bombsPlaced++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void calculateBombsAround(MihMineSweeperProvider mihMineSweeperProvider) {
|
||||
for (int r = 0; r < mihMineSweeperProvider.rowCount; r++) {
|
||||
for (int c = 0; c < mihMineSweeperProvider.columnCount; c++) {
|
||||
if (!board[r][c].hasBomb) {
|
||||
int count = 0;
|
||||
|
||||
// Check the 8 neighbors
|
||||
for (int i = -1; i <= 1; i++) {
|
||||
for (int j = -1; j <= 1; j++) {
|
||||
if (i == 0 && j == 0) continue; // Skip the current square
|
||||
|
||||
int neighborR = r + i;
|
||||
int neighborC = c + j;
|
||||
|
||||
// Check if neighbor is within bounds
|
||||
if (neighborR >= 0 &&
|
||||
neighborR < mihMineSweeperProvider.rowCount &&
|
||||
neighborC >= 0 &&
|
||||
neighborC < mihMineSweeperProvider.columnCount) {
|
||||
if (board[neighborR][neighborC].hasBomb) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
board[r][c].bombsAround = count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handles recursive opening of zero-squares
|
||||
void _expandZeros(
|
||||
MihMineSweeperProvider mihMineSweeperProvider, int r, int c) {
|
||||
if (r < 0 ||
|
||||
r >= mihMineSweeperProvider.rowCount ||
|
||||
c < 0 ||
|
||||
c >= mihMineSweeperProvider.columnCount ||
|
||||
board[r][c].isOpened) {
|
||||
return;
|
||||
}
|
||||
|
||||
BoardSquare square = board[r][c];
|
||||
|
||||
// Open the current square
|
||||
square.isOpened = true;
|
||||
squaresLeft--;
|
||||
|
||||
// If it's a zero square, recursively call for neighbors
|
||||
if (square.bombsAround == 0) {
|
||||
// Check all 8 neighbors (not just 4 sides, for standard Minesweeper expansion)
|
||||
for (int i = -1; i <= 1; i++) {
|
||||
for (int j = -1; j <= 1; j++) {
|
||||
_expandZeros(mihMineSweeperProvider, r + i, c + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleTap(MihMineSweeperProvider mihMineSweeperProvider, int r, int c) {
|
||||
if (isGameOver || board[r][c].isOpened || board[r][c].isFlagged) {
|
||||
return;
|
||||
}
|
||||
// 1. Check for bomb (LOSS)
|
||||
if (board[r][c].hasBomb) {
|
||||
setState(() {
|
||||
board[r][c].isOpened = true;
|
||||
isGameOver = true;
|
||||
// lose alert
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return MihPackageAlert(
|
||||
alertIcon: Icon(
|
||||
FontAwesomeIcons.bomb,
|
||||
color: MihColors.getRedColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||
size: 100,
|
||||
),
|
||||
alertTitle: "Better Luck Next Time",
|
||||
alertBody: Column(
|
||||
children: [
|
||||
Text(
|
||||
"Your lost this game of MIH MineSweeper!!!",
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
color: MihColors.getSecondaryColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode ==
|
||||
"Dark"),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Wrap(
|
||||
runAlignment: WrapAlignment.center,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
spacing: 10,
|
||||
runSpacing: 10,
|
||||
children: [
|
||||
MihButton(
|
||||
onPressed: () {
|
||||
setState(
|
||||
() => initializeBoard(mihMineSweeperProvider));
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
buttonColor: MihColors.getGreenColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode ==
|
||||
"Dark"),
|
||||
width: 300,
|
||||
child: Text(
|
||||
"New Game",
|
||||
style: TextStyle(
|
||||
color: MihColors.getPrimaryColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode ==
|
||||
"Dark"),
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
alertColour: MihColors.getRedColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 2. Open square and handle expansion (RECURSION)
|
||||
if (board[r][c].bombsAround == 0) {
|
||||
// Start recursive expansion
|
||||
_expandZeros(mihMineSweeperProvider, r, c);
|
||||
} else {
|
||||
// Just open the single square
|
||||
board[r][c].isOpened = true;
|
||||
squaresLeft--;
|
||||
}
|
||||
// 3. Check for win
|
||||
_checkWinCondition(mihMineSweeperProvider);
|
||||
// Update the UI
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
void handleLongPress(int r, int c) {
|
||||
if (isGameOver || board[r][c].isOpened) {
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
// Toggle the flag status
|
||||
board[r][c].isFlagged = !board[r][c].isFlagged;
|
||||
});
|
||||
}
|
||||
|
||||
// --- GAME ACTION LOGIC ---
|
||||
void _checkWinCondition(MihMineSweeperProvider mihMineSweeperProvider) {
|
||||
// Game is won if all non-mine squares are opened.
|
||||
if (squaresLeft <= mihMineSweeperProvider.totalMines) {
|
||||
isGameWon = true;
|
||||
isGameOver = true;
|
||||
// win alert
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return MihPackageAlert(
|
||||
alertIcon: Icon(
|
||||
Icons.celebration,
|
||||
color: MihColors.getGreenColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||
size: 100,
|
||||
),
|
||||
alertTitle: "Congradulations",
|
||||
alertBody: Column(
|
||||
children: [
|
||||
Text(
|
||||
"Your won this game of MIH MineSweeper!!!",
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
color: MihColors.getSecondaryColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Wrap(
|
||||
runAlignment: WrapAlignment.center,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
spacing: 10,
|
||||
runSpacing: 10,
|
||||
children: [
|
||||
MihButton(
|
||||
onPressed: () {
|
||||
setState(() => initializeBoard(mihMineSweeperProvider));
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
buttonColor: MihColors.getGreenColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode ==
|
||||
"Dark"),
|
||||
width: 300,
|
||||
child: Text(
|
||||
"New Game",
|
||||
style: TextStyle(
|
||||
color: MihColors.getPrimaryColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode ==
|
||||
"Dark"),
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
alertColour: MihColors.getGreenColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Color? getDifficultyColor() {
|
||||
String mode = modeController.text;
|
||||
switch (mode) {
|
||||
case "Easy":
|
||||
return MihColors.getGreenColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode == "Dark",
|
||||
);
|
||||
case "Normal":
|
||||
return MihColors.getOrangeColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode == "Dark",
|
||||
);
|
||||
case "Hard":
|
||||
return MihColors.getRedColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode == "Dark",
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
modeController.text = "Easy";
|
||||
// showStartGameWindow(context.read<MihMineSweeperProvider>());
|
||||
// initializeBoard();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
// This method is safe for calling showDialog or reading provider values.
|
||||
if (_isFirstLoad) {
|
||||
// 1. Get the provider safely.
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final mihMineSweeperProvider = context.read<MihMineSweeperProvider>();
|
||||
// board = List.generate(
|
||||
// mihMineSweeperProvider.rowCount,
|
||||
// (i) => List.generate(
|
||||
// mihMineSweeperProvider.columnCount,
|
||||
// (j) => BoardSquare(),
|
||||
// ),
|
||||
// );
|
||||
// 2. Show the dialog to get initial game settings.
|
||||
// The user selection in the dialog will call initializeBoard().
|
||||
showStartGameWindow(mihMineSweeperProvider);
|
||||
});
|
||||
// 3. Set flag to prevent showing the dialog on subsequent dependency changes
|
||||
_isFirstLoad = false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<MihMineSweeperProvider>(
|
||||
builder: (BuildContext context,
|
||||
MihMineSweeperProvider mihMineSweeperProvider, Widget? child) {
|
||||
return Stack(
|
||||
alignment: Alignment.topCenter,
|
||||
children: [
|
||||
MihSingleChildScroll(
|
||||
child: board.isEmpty && squaresLeft < 0
|
||||
// Start Up Message before setting up game
|
||||
? Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const SizedBox(height: 50),
|
||||
Icon(
|
||||
MihIcons.mineSweeper,
|
||||
size: 165,
|
||||
color: MihColors.getSecondaryColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode ==
|
||||
"Dark"),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"Welcom to MIH MineSweeper, the first game of MIH.",
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.visible,
|
||||
style: TextStyle(
|
||||
fontSize: 25,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: MihColors.getSecondaryColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode ==
|
||||
"Dark"),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
Center(
|
||||
child: RichText(
|
||||
textAlign: TextAlign.center,
|
||||
text: TextSpan(
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.normal,
|
||||
color: MihColors.getSecondaryColor(
|
||||
MzansiInnovationHub.of(context)!
|
||||
.theme
|
||||
.mode ==
|
||||
"Dark"),
|
||||
),
|
||||
children: [
|
||||
TextSpan(text: "Press "),
|
||||
WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child: Icon(
|
||||
Icons.menu,
|
||||
size: 20,
|
||||
color: MihColors.getSecondaryColor(
|
||||
MzansiInnovationHub.of(context)!
|
||||
.theme
|
||||
.mode ==
|
||||
"Dark"),
|
||||
),
|
||||
),
|
||||
TextSpan(text: " to start a new game."),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
// Display Game Board when game started
|
||||
: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
// Display game status
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: Text(
|
||||
'Mines: ${mihMineSweeperProvider.totalMines}',
|
||||
textAlign: TextAlign.left,
|
||||
style: const TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: Text(
|
||||
modeController.text,
|
||||
textAlign: TextAlign.right,
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: getDifficultyColor(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// const SizedBox(
|
||||
// height: 30,
|
||||
// ),
|
||||
// The Board Grid
|
||||
SizedBox(
|
||||
width: mihMineSweeperProvider.columnCount *
|
||||
40.0, // Control size based on columns
|
||||
height: mihMineSweeperProvider.rowCount *
|
||||
40.0, // Control size based on rows
|
||||
child: GridView.builder(
|
||||
physics:
|
||||
const NeverScrollableScrollPhysics(), // Prevent scrolling
|
||||
gridDelegate:
|
||||
SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount:
|
||||
mihMineSweeperProvider.columnCount,
|
||||
crossAxisSpacing: 0,
|
||||
mainAxisSpacing: 0,
|
||||
),
|
||||
itemCount: mihMineSweeperProvider.rowCount *
|
||||
mihMineSweeperProvider.columnCount,
|
||||
itemBuilder: (context, index) {
|
||||
int r = index ~/
|
||||
mihMineSweeperProvider
|
||||
.columnCount; // Integer division for row
|
||||
int c = index %
|
||||
mihMineSweeperProvider
|
||||
.columnCount; // Remainder for column
|
||||
|
||||
return MineTile(
|
||||
square: board[r][c],
|
||||
onTap: () =>
|
||||
handleTap(mihMineSweeperProvider, r, c),
|
||||
onLongPress: () => handleLongPress(r, c),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
right: 10,
|
||||
bottom: 10,
|
||||
child: MihFloatingMenu(
|
||||
animatedIcon: AnimatedIcons.menu_close,
|
||||
children: [
|
||||
SpeedDialChild(
|
||||
child: Icon(
|
||||
Icons.add,
|
||||
color: MihColors.getPrimaryColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode ==
|
||||
"Dark"),
|
||||
),
|
||||
label: board.isEmpty && squaresLeft < 0
|
||||
? "Start Game"
|
||||
: "Reset Game",
|
||||
labelBackgroundColor: MihColors.getGreenColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode ==
|
||||
"Dark"),
|
||||
labelStyle: TextStyle(
|
||||
color: MihColors.getPrimaryColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode ==
|
||||
"Dark"),
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
backgroundColor: MihColors.getGreenColor(
|
||||
MzansiInnovationHub.of(context)!.theme.mode ==
|
||||
"Dark"),
|
||||
onTap: () {
|
||||
showStartGameWindow(mihMineSweeperProvider);
|
||||
},
|
||||
)
|
||||
]),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user