NEW: MIH MineSweeper Package pt2
This commit is contained in:
@@ -0,0 +1,42 @@
|
|||||||
|
class MinesweeperPlayerScore {
|
||||||
|
String app_id;
|
||||||
|
String username;
|
||||||
|
String proPicUrl;
|
||||||
|
String difficulty;
|
||||||
|
String game_time;
|
||||||
|
double game_score;
|
||||||
|
DateTime played_date;
|
||||||
|
|
||||||
|
MinesweeperPlayerScore({
|
||||||
|
required this.app_id,
|
||||||
|
required this.username,
|
||||||
|
required this.proPicUrl,
|
||||||
|
required this.difficulty,
|
||||||
|
required this.game_time,
|
||||||
|
required this.game_score,
|
||||||
|
required this.played_date,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory MinesweeperPlayerScore.fromJson(Map<String, dynamic> json) {
|
||||||
|
return MinesweeperPlayerScore(
|
||||||
|
app_id: json['app_id'],
|
||||||
|
username: json['username'],
|
||||||
|
proPicUrl: json['proPicUrl'],
|
||||||
|
difficulty: json['difficulty'],
|
||||||
|
game_time: json['game_time'],
|
||||||
|
game_score: json['game_score'],
|
||||||
|
played_date: DateTime.parse(json['played_date']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'app_id': app_id,
|
||||||
|
'username': username,
|
||||||
|
'proPicUrl': proPicUrl,
|
||||||
|
'difficulty': difficulty,
|
||||||
|
'game_time': game_score,
|
||||||
|
'played_date': played_date.toIso8601String(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ class MihDropdownField extends StatefulWidget {
|
|||||||
final bool editable;
|
final bool editable;
|
||||||
final bool enableSearch;
|
final bool enableSearch;
|
||||||
final FormFieldValidator<String>? validator;
|
final FormFieldValidator<String>? validator;
|
||||||
|
final Function(String?)? onSelected;
|
||||||
|
|
||||||
const MihDropdownField({
|
const MihDropdownField({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -20,6 +21,7 @@ class MihDropdownField extends StatefulWidget {
|
|||||||
required this.editable,
|
required this.editable,
|
||||||
required this.enableSearch,
|
required this.enableSearch,
|
||||||
this.validator,
|
this.validator,
|
||||||
|
this.onSelected,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -153,6 +155,7 @@ class _MihDropdownFieldState extends State<MihDropdownField> {
|
|||||||
),
|
),
|
||||||
onSelected: (String? selectedValue) {
|
onSelected: (String? selectedValue) {
|
||||||
field.didChange(selectedValue);
|
field.didChange(selectedValue);
|
||||||
|
widget.onSelected?.call(selectedValue);
|
||||||
},
|
},
|
||||||
menuStyle: MenuStyle(
|
menuStyle: MenuStyle(
|
||||||
backgroundColor: WidgetStatePropertyAll(
|
backgroundColor: WidgetStatePropertyAll(
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_components/mih_objects/minesweeper_player_score.dart';
|
||||||
|
|
||||||
class MihMineSweeperProvider extends ChangeNotifier {
|
class MihMineSweeperProvider extends ChangeNotifier {
|
||||||
String difficulty;
|
String difficulty;
|
||||||
@@ -6,9 +7,12 @@ class MihMineSweeperProvider extends ChangeNotifier {
|
|||||||
int rowCount;
|
int rowCount;
|
||||||
int columnCount;
|
int columnCount;
|
||||||
int totalMines;
|
int totalMines;
|
||||||
|
List<MinesweeperPlayerScore>? leaderboard;
|
||||||
|
List<MinesweeperPlayerScore>? myScoreboard;
|
||||||
|
List<ImageProvider<Object>?> leaderboardUserPictures = [];
|
||||||
|
|
||||||
MihMineSweeperProvider({
|
MihMineSweeperProvider({
|
||||||
this.difficulty = "Normal",
|
this.difficulty = "Easy",
|
||||||
this.toolIndex = 0,
|
this.toolIndex = 0,
|
||||||
this.rowCount = 10,
|
this.rowCount = 10,
|
||||||
this.columnCount = 10,
|
this.columnCount = 10,
|
||||||
@@ -16,7 +20,7 @@ class MihMineSweeperProvider extends ChangeNotifier {
|
|||||||
});
|
});
|
||||||
|
|
||||||
void reset() {
|
void reset() {
|
||||||
difficulty = "Normal";
|
difficulty = "Easy";
|
||||||
toolIndex = 0;
|
toolIndex = 0;
|
||||||
rowCount = 10;
|
rowCount = 10;
|
||||||
columnCount = 10;
|
columnCount = 10;
|
||||||
@@ -48,4 +52,30 @@ class MihMineSweeperProvider extends ChangeNotifier {
|
|||||||
this.totalMines = totalMines;
|
this.totalMines = totalMines;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setLeaderboard({required List<MinesweeperPlayerScore>? leaderboard}) {
|
||||||
|
if (leaderboard == null) {
|
||||||
|
this.leaderboard = [];
|
||||||
|
} else {
|
||||||
|
this.leaderboard = leaderboard;
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMyScoreboard({
|
||||||
|
required List<MinesweeperPlayerScore>? myScoreboard,
|
||||||
|
}) {
|
||||||
|
if (myScoreboard == null) {
|
||||||
|
this.myScoreboard = [];
|
||||||
|
} else {
|
||||||
|
this.myScoreboard = myScoreboard;
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLeaderboardUserPictures(
|
||||||
|
{required List<ImageProvider<Object>?> leaderboardUserPictures}) {
|
||||||
|
this.leaderboardUserPictures = leaderboardUserPictures;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -422,7 +422,7 @@ class MihGoRouter {
|
|||||||
return MIHPrintPreview(arguments: args!);
|
return MIHPrintPreview(arguments: args!);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
// ========================== MIH Calculator ==================================
|
// ========================== MIH Minesweeper ==================================
|
||||||
GoRoute(
|
GoRoute(
|
||||||
name: "mihMinesweeper",
|
name: "mihMinesweeper",
|
||||||
path: MihGoRouterPaths.mihMineSweeper,
|
path: MihGoRouterPaths.mihMineSweeper,
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/main.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_circle_avatar.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_components/mih_providers/mih_mine_sweeper_provider.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_components/mih_providers/mzansi_profile_provider.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class BuildMinesweeperLeaderboardList extends StatefulWidget {
|
||||||
|
const BuildMinesweeperLeaderboardList({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<BuildMinesweeperLeaderboardList> createState() =>
|
||||||
|
_BuildMinesweeperLeaderboardListState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BuildMinesweeperLeaderboardListState
|
||||||
|
extends State<BuildMinesweeperLeaderboardList> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Consumer2<MzansiProfileProvider, MihMineSweeperProvider>(
|
||||||
|
builder: (BuildContext context, MzansiProfileProvider profileProvider,
|
||||||
|
MihMineSweeperProvider mineSweeperProvider, Widget? child) {
|
||||||
|
return ListView.separated(
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
separatorBuilder: (BuildContext context, index) {
|
||||||
|
return Divider(
|
||||||
|
color: MihColors.getSecondaryColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
itemCount: mineSweeperProvider.leaderboard!.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return ListTile(
|
||||||
|
leading: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"#${index + 1}",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 25,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
MihCircleAvatar(
|
||||||
|
key: UniqueKey(),
|
||||||
|
imageFile:
|
||||||
|
mineSweeperProvider.leaderboardUserPictures.isNotEmpty
|
||||||
|
? mineSweeperProvider.leaderboardUserPictures[index]
|
||||||
|
: null,
|
||||||
|
width: 60,
|
||||||
|
editable: false,
|
||||||
|
fileNameController: null,
|
||||||
|
userSelectedfile: null,
|
||||||
|
frameColor: MihColors.getSecondaryColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||||
|
backgroundColor: MihColors.getPrimaryColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||||
|
onChange: () {},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
"${mineSweeperProvider.leaderboard![index].username}${profileProvider.user!.username == mineSweeperProvider.leaderboard![index].username ? " (You)" : ""}",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: MihColors.getSecondaryColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
"Score: ${mineSweeperProvider.leaderboard![index].game_score}\nTime: ${mineSweeperProvider.leaderboard![index].game_time}",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
// fontWeight: FontWeight.bold,
|
||||||
|
color: MihColors.getSecondaryColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/main.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_components/mih_providers/mih_mine_sweeper_provider.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_components/mih_providers/mzansi_profile_provider.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class BuildMyScoreBoardList extends StatefulWidget {
|
||||||
|
const BuildMyScoreBoardList({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<BuildMyScoreBoardList> createState() =>
|
||||||
|
_BuildMinesweeperLeaderboardListState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BuildMinesweeperLeaderboardListState
|
||||||
|
extends State<BuildMyScoreBoardList> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Consumer2<MzansiProfileProvider, MihMineSweeperProvider>(
|
||||||
|
builder: (BuildContext context, MzansiProfileProvider profileProvider,
|
||||||
|
MihMineSweeperProvider mineSweeperProvider, Widget? child) {
|
||||||
|
return ListView.separated(
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
separatorBuilder: (BuildContext context, index) {
|
||||||
|
return Divider(
|
||||||
|
color: MihColors.getSecondaryColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
itemCount: mineSweeperProvider.myScoreboard!.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return ListTile(
|
||||||
|
leading: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"#${index + 1}",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 25,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
"Score: ${mineSweeperProvider.myScoreboard![index].game_score}",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: MihColors.getSecondaryColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
"Time: ${mineSweeperProvider.myScoreboard![index].game_time}",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
// fontWeight: FontWeight.bold,
|
||||||
|
color: MihColors.getSecondaryColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:ken_logger/ken_logger.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/main.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_circle_avatar.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
|
||||||
|
import 'package:redacted/redacted.dart';
|
||||||
|
|
||||||
|
class LeaderboardUserRanking extends StatelessWidget {
|
||||||
|
final int index;
|
||||||
|
final String proPicUrl;
|
||||||
|
final String username;
|
||||||
|
final dynamic gameScore;
|
||||||
|
final String gameTime;
|
||||||
|
final bool isCurrentUser;
|
||||||
|
final Future<ImageProvider<Object>?> Function(String) getUserPicture;
|
||||||
|
|
||||||
|
const LeaderboardUserRanking({
|
||||||
|
super.key,
|
||||||
|
required this.index,
|
||||||
|
required this.proPicUrl,
|
||||||
|
required this.username,
|
||||||
|
required this.gameScore,
|
||||||
|
required this.gameTime,
|
||||||
|
required this.isCurrentUser,
|
||||||
|
required this.getUserPicture,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FutureBuilder(
|
||||||
|
future: getUserPicture(proPicUrl),
|
||||||
|
builder: (context, asyncSnapshot) {
|
||||||
|
bool isLoading =
|
||||||
|
asyncSnapshot.connectionState == ConnectionState.waiting;
|
||||||
|
|
||||||
|
KenLogger.success("URL: ${asyncSnapshot.data.toString()}");
|
||||||
|
return ListTile(
|
||||||
|
leading: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"#${index + 1}",
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 25,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
MihCircleAvatar(
|
||||||
|
key: ValueKey(asyncSnapshot.data
|
||||||
|
.toString()), // Use ValueKey for stable identity
|
||||||
|
imageFile: asyncSnapshot.data,
|
||||||
|
width: 60,
|
||||||
|
editable: false,
|
||||||
|
fileNameController: null,
|
||||||
|
userSelectedfile: null,
|
||||||
|
frameColor: MihColors.getSecondaryColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||||
|
backgroundColor: MihColors.getPrimaryColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||||
|
onChange: () {},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
"$username${isCurrentUser ? " (You)" : ""}",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: MihColors.getSecondaryColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||||
|
),
|
||||||
|
).redacted(context: context, redact: isLoading),
|
||||||
|
subtitle: Text(
|
||||||
|
"Score: $gameScore\nTime: $gameTime",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
color: MihColors.getSecondaryColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||||
|
),
|
||||||
|
).redacted(context: context, redact: isLoading),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
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_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_form.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_window.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:provider/provider.dart';
|
||||||
|
|
||||||
|
class MihMineSweeperStartGameWindow extends StatefulWidget {
|
||||||
|
final void Function()? onPressed;
|
||||||
|
const MihMineSweeperStartGameWindow({
|
||||||
|
super.key,
|
||||||
|
required this.onPressed,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MihMineSweeperStartGameWindow> createState() =>
|
||||||
|
_MihMineSweeperStartGameWindowState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MihMineSweeperStartGameWindowState
|
||||||
|
extends State<MihMineSweeperStartGameWindow> {
|
||||||
|
TextEditingController modeController = TextEditingController();
|
||||||
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
|
void applyGameSettings(MihMineSweeperProvider mihMineSweeperProvider) {
|
||||||
|
mihMineSweeperProvider.setDifficulty(modeController.text);
|
||||||
|
switch (mihMineSweeperProvider.difficulty) {
|
||||||
|
case ("Very Easy"):
|
||||||
|
mihMineSweeperProvider.setRowCount(6);
|
||||||
|
mihMineSweeperProvider.setCoulmnCount(6);
|
||||||
|
mihMineSweeperProvider.setTotalMines(5);
|
||||||
|
// mihMineSweeperProvider.setRowCount(5);
|
||||||
|
// mihMineSweeperProvider.setCoulmnCount(5);
|
||||||
|
// mihMineSweeperProvider.setTotalMines(3);
|
||||||
|
break;
|
||||||
|
case ("Easy"):
|
||||||
|
mihMineSweeperProvider.setRowCount(8);
|
||||||
|
mihMineSweeperProvider.setCoulmnCount(8);
|
||||||
|
mihMineSweeperProvider.setTotalMines(10);
|
||||||
|
// mihMineSweeperProvider.setRowCount(10);
|
||||||
|
// mihMineSweeperProvider.setCoulmnCount(10);
|
||||||
|
// mihMineSweeperProvider.setTotalMines(15);
|
||||||
|
break;
|
||||||
|
case ("Intermediate"):
|
||||||
|
mihMineSweeperProvider.setRowCount(10);
|
||||||
|
mihMineSweeperProvider.setCoulmnCount(10);
|
||||||
|
mihMineSweeperProvider.setTotalMines(18);
|
||||||
|
// mihMineSweeperProvider.setRowCount(15);
|
||||||
|
// mihMineSweeperProvider.setCoulmnCount(10);
|
||||||
|
// mihMineSweeperProvider.setTotalMines(23);
|
||||||
|
break;
|
||||||
|
case ("Hard"):
|
||||||
|
mihMineSweeperProvider.setRowCount(12);
|
||||||
|
mihMineSweeperProvider.setCoulmnCount(10);
|
||||||
|
mihMineSweeperProvider.setTotalMines(30);
|
||||||
|
// mihMineSweeperProvider.setRowCount(20);
|
||||||
|
// mihMineSweeperProvider.setCoulmnCount(10);
|
||||||
|
// mihMineSweeperProvider.setTotalMines(30);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String getModeConfig() {
|
||||||
|
switch (modeController.text) {
|
||||||
|
case ("Very Easy"):
|
||||||
|
return "Columns: 6\nRows: 6\nBombs: 5";
|
||||||
|
case ("Easy"):
|
||||||
|
return "Columns: 8\nRows: 8\nBombs: 10";
|
||||||
|
case ("Intermediate"):
|
||||||
|
return "Columns: 10\nRows: 10\nBombs: 18";
|
||||||
|
case ("Hard"):
|
||||||
|
return "Columns: 10\nRows: 12\nBombs: 30";
|
||||||
|
default:
|
||||||
|
return "Error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onModeChanged() {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
modeController.removeListener(_onModeChanged);
|
||||||
|
modeController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
modeController.text = context.read<MihMineSweeperProvider>().difficulty;
|
||||||
|
modeController.addListener(_onModeChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MihPackageWindow(
|
||||||
|
fullscreen: false,
|
||||||
|
windowTitle: "New Game Settings",
|
||||||
|
onWindowTapClose: () {
|
||||||
|
context.pop();
|
||||||
|
},
|
||||||
|
windowBody: Consumer<MihMineSweeperProvider>(
|
||||||
|
builder: (BuildContext context,
|
||||||
|
MihMineSweeperProvider mihMineSweeperProvider, Widget? child) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
MihForm(
|
||||||
|
formKey: _formKey,
|
||||||
|
formFields: [
|
||||||
|
MihDropdownField(
|
||||||
|
controller: modeController,
|
||||||
|
hintText: "Difficulty",
|
||||||
|
dropdownOptions: [
|
||||||
|
"Very Easy",
|
||||||
|
"Easy",
|
||||||
|
"Intermediate",
|
||||||
|
"Hard"
|
||||||
|
],
|
||||||
|
requiredText: true,
|
||||||
|
editable: true,
|
||||||
|
enableSearch: false,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Text(
|
||||||
|
getModeConfig(),
|
||||||
|
style: TextStyle(
|
||||||
|
color: MihColors.getSecondaryColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode ==
|
||||||
|
"Dark"),
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 25),
|
||||||
|
Center(
|
||||||
|
child: MihButton(
|
||||||
|
onPressed: () {
|
||||||
|
applyGameSettings(mihMineSweeperProvider);
|
||||||
|
context.pop();
|
||||||
|
widget.onPressed?.call();
|
||||||
|
},
|
||||||
|
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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,8 +4,12 @@ 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.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_action.dart';
|
||||||
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_tools.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_banner_ad_provider.dart';
|
||||||
import 'package:mzansi_innovation_hub/mih_components/mih_providers/mih_mine_sweeper_provider.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/mih_mine_sweeper_leader_board.dart';
|
||||||
import 'package:mzansi_innovation_hub/mih_packages/mine_sweeper/package_tools/mine_sweeper_game.dart';
|
import 'package:mzansi_innovation_hub/mih_packages/mine_sweeper/package_tools/mine_sweeper_game.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_packages/mine_sweeper/package_tools/mine_sweeper_quick_start_guide.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_packages/mine_sweeper/package_tools/my_score_board.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class MihMineSweeper extends StatefulWidget {
|
class MihMineSweeper extends StatefulWidget {
|
||||||
@@ -16,6 +20,14 @@ class MihMineSweeper extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _MihMineSweeperState extends State<MihMineSweeper> {
|
class _MihMineSweeperState extends State<MihMineSweeper> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
|
context.read<MihBannerAdProvider>().loadBannerAd();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MihPackage(
|
return MihPackage(
|
||||||
@@ -35,9 +47,12 @@ class _MihMineSweeperState extends State<MihMineSweeper> {
|
|||||||
icon: const Icon(Icons.arrow_back),
|
icon: const Icon(Icons.arrow_back),
|
||||||
iconSize: 35,
|
iconSize: 35,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
MihMineSweeperProvider mineSweeperProvider =
|
||||||
|
context.read<MihMineSweeperProvider>();
|
||||||
|
mineSweeperProvider.setToolIndex(0);
|
||||||
|
mineSweeperProvider.setDifficulty("Easy");
|
||||||
context.goNamed(
|
context.goNamed(
|
||||||
'mihHome',
|
'mihHome',
|
||||||
extra: true,
|
|
||||||
);
|
);
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
},
|
},
|
||||||
@@ -49,6 +64,15 @@ class _MihMineSweeperState extends State<MihMineSweeper> {
|
|||||||
temp[const Icon(FontAwesomeIcons.bomb)] = () {
|
temp[const Icon(FontAwesomeIcons.bomb)] = () {
|
||||||
context.read<MihMineSweeperProvider>().setToolIndex(0);
|
context.read<MihMineSweeperProvider>().setToolIndex(0);
|
||||||
};
|
};
|
||||||
|
temp[const Icon(Icons.leaderboard_rounded)] = () {
|
||||||
|
context.read<MihMineSweeperProvider>().setToolIndex(1);
|
||||||
|
};
|
||||||
|
temp[const Icon(Icons.perm_identity_rounded)] = () {
|
||||||
|
context.read<MihMineSweeperProvider>().setToolIndex(2);
|
||||||
|
};
|
||||||
|
temp[const Icon(Icons.rule_rounded)] = () {
|
||||||
|
context.read<MihMineSweeperProvider>().setToolIndex(3);
|
||||||
|
};
|
||||||
return MihPackageTools(
|
return MihPackageTools(
|
||||||
tools: temp,
|
tools: temp,
|
||||||
selcetedIndex: context.watch<MihMineSweeperProvider>().toolIndex,
|
selcetedIndex: context.watch<MihMineSweeperProvider>().toolIndex,
|
||||||
@@ -57,7 +81,10 @@ class _MihMineSweeperState extends State<MihMineSweeper> {
|
|||||||
|
|
||||||
List<String> getToolTitle() {
|
List<String> getToolTitle() {
|
||||||
List<String> toolTitles = [
|
List<String> toolTitles = [
|
||||||
"MineSweeper",
|
"Minesweeper",
|
||||||
|
"Leader Board",
|
||||||
|
"My Scores",
|
||||||
|
"Guide",
|
||||||
];
|
];
|
||||||
return toolTitles;
|
return toolTitles;
|
||||||
}
|
}
|
||||||
@@ -65,6 +92,9 @@ class _MihMineSweeperState extends State<MihMineSweeper> {
|
|||||||
List<Widget> getToolBody() {
|
List<Widget> getToolBody() {
|
||||||
List<Widget> toolBodies = [
|
List<Widget> toolBodies = [
|
||||||
const MineSweeperGame(),
|
const MineSweeperGame(),
|
||||||
|
const MihMineSweeperLeaderBoard(),
|
||||||
|
const MyScoreBoard(),
|
||||||
|
const MineSweeperQuickStartGuide(),
|
||||||
];
|
];
|
||||||
return toolBodies;
|
return toolBodies;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ class _MihMineSweeperTileState extends State<MihMineSweeperTile> {
|
|||||||
return MihPackageTile(
|
return MihPackageTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.goNamed(
|
context.goNamed(
|
||||||
"mihMineSweeper",
|
"mihMinesweeper",
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
appName: "MineSweeper",
|
appName: "Minesweeper",
|
||||||
appIcon: Icon(
|
appIcon: Icon(
|
||||||
MihIcons.mineSweeper,
|
MihIcons.mineSweeper,
|
||||||
color: MihColors.getSecondaryColor(
|
color: MihColors.getSecondaryColor(
|
||||||
|
|||||||
@@ -0,0 +1,202 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:ken_logger/ken_logger.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/main.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_icons.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_tool_body.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_loading_circle.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/builders/build_minesweeper_leaderboard_list.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_services/mih_file_services.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_services/mih_minesweeper_services.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_services/mih_validation_services.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class MihMineSweeperLeaderBoard extends StatefulWidget {
|
||||||
|
const MihMineSweeperLeaderBoard({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MihMineSweeperLeaderBoard> createState() =>
|
||||||
|
_MihMineSweeperLeaderBoardState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MihMineSweeperLeaderBoardState extends State<MihMineSweeperLeaderBoard> {
|
||||||
|
TextEditingController filterController = TextEditingController();
|
||||||
|
|
||||||
|
Future<void> initialiseLeaderboard() async {
|
||||||
|
MihMineSweeperProvider mineSweeperProvider =
|
||||||
|
context.read<MihMineSweeperProvider>();
|
||||||
|
filterController.text = mineSweeperProvider.difficulty;
|
||||||
|
if (mineSweeperProvider.leaderboard == null ||
|
||||||
|
mineSweeperProvider.leaderboard!.isEmpty) {
|
||||||
|
KenLogger.success("getting data");
|
||||||
|
await MihMinesweeperServices().getTop20Leaderboard(mineSweeperProvider);
|
||||||
|
List<ImageProvider<Object>?> userPictures = [];
|
||||||
|
String userPicUrl = "";
|
||||||
|
for (final ranking in mineSweeperProvider.leaderboard!) {
|
||||||
|
userPicUrl =
|
||||||
|
await MihFileApi.getMinioFileUrl(ranking.proPicUrl, context);
|
||||||
|
userPictures.add(NetworkImage(userPicUrl));
|
||||||
|
}
|
||||||
|
mineSweeperProvider.setLeaderboardUserPictures(
|
||||||
|
leaderboardUserPictures: userPictures);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void refreshLeaderBoard(
|
||||||
|
MihMineSweeperProvider mineSweeperProvider, String difficulty) {
|
||||||
|
mineSweeperProvider.setDifficulty(difficulty);
|
||||||
|
mineSweeperProvider.setLeaderboard(leaderboard: null);
|
||||||
|
mineSweeperProvider.setMyScoreboard(myScoreboard: null);
|
||||||
|
initialiseLeaderboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
|
await initialiseLeaderboard();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final double width = MediaQuery.sizeOf(context).width;
|
||||||
|
return Consumer<MihMineSweeperProvider>(
|
||||||
|
builder: (BuildContext context,
|
||||||
|
MihMineSweeperProvider mineSweeperProvider, Widget? child) {
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: () async {
|
||||||
|
refreshLeaderBoard(mineSweeperProvider, filterController.text);
|
||||||
|
},
|
||||||
|
child: MihPackageToolBody(
|
||||||
|
borderOn: false,
|
||||||
|
bodyItem: getBody(width),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget getBody(double width) {
|
||||||
|
return Consumer<MihMineSweeperProvider>(
|
||||||
|
builder: (BuildContext context,
|
||||||
|
MihMineSweeperProvider mineSweeperProvider, Widget? child) {
|
||||||
|
if (mineSweeperProvider.leaderboard == null) {
|
||||||
|
return Center(
|
||||||
|
child: Mihloadingcircle(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return SingleChildScrollView(
|
||||||
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: width / 20),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: MihDropdownField(
|
||||||
|
controller: filterController,
|
||||||
|
hintText: "Leaderboards",
|
||||||
|
dropdownOptions: const [
|
||||||
|
"Very Easy",
|
||||||
|
"Easy",
|
||||||
|
"Intermediate",
|
||||||
|
"Hard",
|
||||||
|
],
|
||||||
|
requiredText: true,
|
||||||
|
editable: true,
|
||||||
|
enableSearch: true,
|
||||||
|
validator: (value) {
|
||||||
|
return MihValidationServices().isEmpty(value);
|
||||||
|
},
|
||||||
|
onSelected: (selection) {
|
||||||
|
refreshLeaderBoard(mineSweeperProvider, selection!);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
mineSweeperProvider.leaderboard!.isEmpty
|
||||||
|
? 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(
|
||||||
|
"Be the first on the leaderboard.",
|
||||||
|
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(
|
||||||
|
FontAwesomeIcons.bomb,
|
||||||
|
size: 20,
|
||||||
|
color: MihColors.getSecondaryColor(
|
||||||
|
MzansiInnovationHub.of(context)!
|
||||||
|
.theme
|
||||||
|
.mode ==
|
||||||
|
"Dark"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextSpan(text: " and start a new game"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: BuildMinesweeperLeaderboardList(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,22 +1,26 @@
|
|||||||
|
import 'dart:async';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
|
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:mzansi_innovation_hub/main.dart';
|
import 'package:mzansi_innovation_hub/main.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_banner_ad.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_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_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_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_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_package_tool_body.dart';
|
||||||
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_single_child_scroll.dart';
|
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_single_child_scroll.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_loading_circle.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_components/mih_providers/mih_banner_ad_provider.dart';
|
||||||
import 'package:mzansi_innovation_hub/mih_components/mih_providers/mih_mine_sweeper_provider.dart';
|
import 'package:mzansi_innovation_hub/mih_components/mih_providers/mih_mine_sweeper_provider.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_components/mih_providers/mzansi_profile_provider.dart';
|
||||||
import 'package:mzansi_innovation_hub/mih_config/mih_colors.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/board_square.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_packages/mine_sweeper/components/mih_mine_sweeper_start_game_window.dart';
|
||||||
import 'package:mzansi_innovation_hub/mih_packages/mine_sweeper/components/mine_tile.dart';
|
import 'package:mzansi_innovation_hub/mih_packages/mine_sweeper/components/mine_tile.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_services/mih_minesweeper_services.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class MineSweeperGame extends StatefulWidget {
|
class MineSweeperGame extends StatefulWidget {
|
||||||
@@ -27,92 +31,115 @@ class MineSweeperGame extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _MineSweeperGameState extends State<MineSweeperGame> {
|
class _MineSweeperGameState extends State<MineSweeperGame> {
|
||||||
TextEditingController modeController = TextEditingController();
|
|
||||||
final _formKey = GlobalKey<FormState>();
|
|
||||||
List<List<BoardSquare>> board = [];
|
List<List<BoardSquare>> board = [];
|
||||||
bool isGameOver = false;
|
bool isGameOver = false;
|
||||||
bool isGameWon = false;
|
bool isGameWon = false;
|
||||||
int squaresLeft = -1;
|
int squaresLeft = -1;
|
||||||
bool _isFirstLoad = true;
|
Timer? _timer;
|
||||||
|
int _milliseconds = 0;
|
||||||
|
bool _isRunning = false;
|
||||||
|
static const int millisecondsPerUpdate = 10;
|
||||||
|
|
||||||
String getModeConfig() {
|
double timeStringToTotalSeconds(String timeString) {
|
||||||
switch (modeController.text) {
|
try {
|
||||||
|
List<String> parts = timeString.split(':');
|
||||||
|
if (parts.length < 4) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
double hours = double.parse(parts[0]);
|
||||||
|
double minutes = double.parse(parts[1]);
|
||||||
|
double seconds = double.parse(parts[2]);
|
||||||
|
double milliseconds = double.parse(parts[3]);
|
||||||
|
double totalSeconds =
|
||||||
|
(hours * 3600) + (minutes * 60) + seconds + (milliseconds / 1000);
|
||||||
|
return totalSeconds;
|
||||||
|
} catch (e) {
|
||||||
|
print("Error parsing time string: $e");
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double calculateGameScore(MihMineSweeperProvider mihMineSweeperProvider) {
|
||||||
|
int scoreConst = 10000;
|
||||||
|
double dificusltyMultiplier;
|
||||||
|
switch (mihMineSweeperProvider.difficulty) {
|
||||||
|
case ("Very Easy"):
|
||||||
|
dificusltyMultiplier = 0.5;
|
||||||
|
break;
|
||||||
case ("Easy"):
|
case ("Easy"):
|
||||||
return "Columns: 10\nRows: 10\nBomds: 15";
|
dificusltyMultiplier = 1.0;
|
||||||
case ("Normal"):
|
break;
|
||||||
return "Columns: 10\nRows: 15\nBomds: 23";
|
case ("Intermediate"):
|
||||||
|
dificusltyMultiplier = 2.5;
|
||||||
|
break;
|
||||||
case ("Hard"):
|
case ("Hard"):
|
||||||
return "Columns: 10\nRows: 20\nBomds: 30";
|
dificusltyMultiplier = 5.0;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return "Error";
|
dificusltyMultiplier = 0.0;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
double rawScore = (scoreConst * dificusltyMultiplier) /
|
||||||
|
timeStringToTotalSeconds(_formatTime());
|
||||||
|
|
||||||
|
String scoreString = rawScore.toStringAsFixed(5);
|
||||||
|
return double.parse(scoreString);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showStartGameWindow(MihMineSweeperProvider mihMineSweeperProvider) {
|
void startTimer() {
|
||||||
|
if (_isRunning) return;
|
||||||
|
_isRunning = true;
|
||||||
|
_timer = Timer.periodic(const Duration(milliseconds: millisecondsPerUpdate),
|
||||||
|
(timer) {
|
||||||
|
setState(() {
|
||||||
|
_milliseconds += millisecondsPerUpdate; // Increment by the interval
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopTimer() {
|
||||||
|
_timer?.cancel();
|
||||||
|
setState(() {
|
||||||
|
_isRunning = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetTimer() {
|
||||||
|
stopTimer(); // Stop the timer first
|
||||||
|
setState(() {
|
||||||
|
_milliseconds = 0; // Reset the time to zero
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
String _formatTime() {
|
||||||
|
Duration duration = Duration(milliseconds: _milliseconds);
|
||||||
|
final int hours = duration.inHours.remainder(60);
|
||||||
|
final int minutes = duration.inMinutes.remainder(60);
|
||||||
|
final int seconds = duration.inSeconds.remainder(60);
|
||||||
|
final int centiseconds = (duration.inMilliseconds.remainder(1000)) ~/ 10;
|
||||||
|
String hoursStr = hours.toString().padLeft(2, '0');
|
||||||
|
String minutesStr = minutes.toString().padLeft(2, '0');
|
||||||
|
String secondsStr = seconds.toString().padLeft(2, '0');
|
||||||
|
String centiStr = centiseconds.toString().padLeft(2, '0');
|
||||||
|
return '$hoursStr:$minutesStr:$secondsStr:$centiStr';
|
||||||
|
}
|
||||||
|
|
||||||
|
void showStartGameWindow(MihMineSweeperProvider mihMineSweeperProvider,
|
||||||
|
MihBannerAdProvider addProvider) {
|
||||||
// easy - 10 * 10 & 15 bombs
|
// easy - 10 * 10 & 15 bombs
|
||||||
// Normal - 10 * 15 & 23 bombs
|
// Intermediate - 10 * 15 & 23 bombs
|
||||||
// Hard - 10 * 20 & 30 bombs
|
// Hard - 10 * 20 & 30 bombs
|
||||||
|
addProvider.loadBannerAd();
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return MihPackageWindow(
|
return MihMineSweeperStartGameWindow(
|
||||||
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: 10),
|
|
||||||
Text(
|
|
||||||
getModeConfig(),
|
|
||||||
style: TextStyle(
|
|
||||||
color: MihColors.getSecondaryColor(
|
|
||||||
MzansiInnovationHub.of(context)!.theme.mode ==
|
|
||||||
"Dark"),
|
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 25),
|
|
||||||
Center(
|
|
||||||
child: MihButton(
|
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(
|
resetTimer();
|
||||||
() => initializeBoard(mihMineSweeperProvider));
|
mihMineSweeperProvider
|
||||||
Navigator.of(context).pop();
|
.setDifficulty(mihMineSweeperProvider.difficulty);
|
||||||
|
setState(() => initializeBoard(mihMineSweeperProvider));
|
||||||
},
|
},
|
||||||
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -137,6 +164,7 @@ class _MineSweeperGameState extends State<MineSweeperGame> {
|
|||||||
isGameOver = false;
|
isGameOver = false;
|
||||||
isGameWon = false;
|
isGameWon = false;
|
||||||
// You'd typically add a call to setState here, but it's in initState.
|
// You'd typically add a call to setState here, but it's in initState.
|
||||||
|
startTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void placeBombs(MihMineSweeperProvider mihMineSweeperProvider) {
|
void placeBombs(MihMineSweeperProvider mihMineSweeperProvider) {
|
||||||
@@ -213,12 +241,18 @@ class _MineSweeperGameState extends State<MineSweeperGame> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleTap(MihMineSweeperProvider mihMineSweeperProvider, int r, int c) {
|
Future<void> handleTap(
|
||||||
|
MzansiProfileProvider profileProvider,
|
||||||
|
MihMineSweeperProvider mihMineSweeperProvider,
|
||||||
|
MihBannerAdProvider adProvider,
|
||||||
|
int r,
|
||||||
|
int c) async {
|
||||||
if (isGameOver || board[r][c].isOpened || board[r][c].isFlagged) {
|
if (isGameOver || board[r][c].isOpened || board[r][c].isFlagged) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 1. Check for bomb (LOSS)
|
// 1. Check for bomb (LOSS)
|
||||||
if (board[r][c].hasBomb) {
|
if (board[r][c].hasBomb) {
|
||||||
|
stopTimer();
|
||||||
setState(() {
|
setState(() {
|
||||||
board[r][c].isOpened = true;
|
board[r][c].isOpened = true;
|
||||||
isGameOver = true;
|
isGameOver = true;
|
||||||
@@ -227,19 +261,34 @@ class _MineSweeperGameState extends State<MineSweeperGame> {
|
|||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return MihPackageAlert(
|
return MihPackageAlert(
|
||||||
alertIcon: Icon(
|
alertIcon: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Icon(
|
||||||
FontAwesomeIcons.bomb,
|
FontAwesomeIcons.bomb,
|
||||||
color: MihColors.getRedColor(
|
color: MihColors.getRedColor(
|
||||||
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||||
size: 100,
|
size: 100,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
alertTitle: "Better Luck Next Time",
|
alertTitle: "Better Luck Next Time",
|
||||||
alertBody: Column(
|
alertBody: Column(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"Your lost this game of MIH MineSweeper!!!",
|
"Your lost this game of MIH MineSweeper!!!",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 15,
|
fontSize: 20,
|
||||||
|
color: MihColors.getSecondaryColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode ==
|
||||||
|
"Dark"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Text(
|
||||||
|
"Please feel free to start a New Game or check out the Leader Board to find out who's the best in Mzansi.",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
color: MihColors.getSecondaryColor(
|
color: MihColors.getSecondaryColor(
|
||||||
MzansiInnovationHub.of(context)!.theme.mode ==
|
MzansiInnovationHub.of(context)!.theme.mode ==
|
||||||
"Dark"),
|
"Dark"),
|
||||||
@@ -254,9 +303,9 @@ class _MineSweeperGameState extends State<MineSweeperGame> {
|
|||||||
children: [
|
children: [
|
||||||
MihButton(
|
MihButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(
|
context.pop();
|
||||||
() => initializeBoard(mihMineSweeperProvider));
|
showStartGameWindow(
|
||||||
Navigator.of(context).pop();
|
mihMineSweeperProvider, adProvider);
|
||||||
},
|
},
|
||||||
buttonColor: MihColors.getGreenColor(
|
buttonColor: MihColors.getGreenColor(
|
||||||
MzansiInnovationHub.of(context)!.theme.mode ==
|
MzansiInnovationHub.of(context)!.theme.mode ==
|
||||||
@@ -273,6 +322,26 @@ class _MineSweeperGameState extends State<MineSweeperGame> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
MihButton(
|
||||||
|
onPressed: () {
|
||||||
|
context.pop();
|
||||||
|
mihMineSweeperProvider.setToolIndex(1);
|
||||||
|
},
|
||||||
|
buttonColor: MihColors.getOrangeColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode ==
|
||||||
|
"Dark"),
|
||||||
|
width: 300,
|
||||||
|
child: Text(
|
||||||
|
"Leader Board",
|
||||||
|
style: TextStyle(
|
||||||
|
color: MihColors.getPrimaryColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode ==
|
||||||
|
"Dark"),
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -295,7 +364,7 @@ class _MineSweeperGameState extends State<MineSweeperGame> {
|
|||||||
squaresLeft--;
|
squaresLeft--;
|
||||||
}
|
}
|
||||||
// 3. Check for win
|
// 3. Check for win
|
||||||
_checkWinCondition(mihMineSweeperProvider);
|
_checkWinCondition(profileProvider, mihMineSweeperProvider, adProvider);
|
||||||
// Update the UI
|
// Update the UI
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
@@ -311,9 +380,14 @@ class _MineSweeperGameState extends State<MineSweeperGame> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- GAME ACTION LOGIC ---
|
// --- GAME ACTION LOGIC ---
|
||||||
void _checkWinCondition(MihMineSweeperProvider mihMineSweeperProvider) {
|
Future<void> _checkWinCondition(
|
||||||
|
MzansiProfileProvider profileProvider,
|
||||||
|
MihMineSweeperProvider mihMineSweeperProvider,
|
||||||
|
MihBannerAdProvider adProvider,
|
||||||
|
) async {
|
||||||
// Game is won if all non-mine squares are opened.
|
// Game is won if all non-mine squares are opened.
|
||||||
if (squaresLeft <= mihMineSweeperProvider.totalMines) {
|
if (squaresLeft <= mihMineSweeperProvider.totalMines) {
|
||||||
|
stopTimer();
|
||||||
isGameWon = true;
|
isGameWon = true;
|
||||||
isGameOver = true;
|
isGameOver = true;
|
||||||
// win alert
|
// win alert
|
||||||
@@ -327,13 +401,41 @@ class _MineSweeperGameState extends State<MineSweeperGame> {
|
|||||||
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||||
size: 100,
|
size: 100,
|
||||||
),
|
),
|
||||||
alertTitle: "Congradulations",
|
alertTitle: "Congratulations",
|
||||||
alertBody: Column(
|
alertBody: Column(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"Your won this game of MIH MineSweeper!!!",
|
"Your won this game of MIH MineSweeper!!!",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 15,
|
fontSize: 20,
|
||||||
|
color: MihColors.getSecondaryColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
// Text(
|
||||||
|
// "You took ${_formatTime()} to complete the game on ${mihMineSweeperProvider.difficulty} mode.",
|
||||||
|
// style: TextStyle(
|
||||||
|
// fontSize: 15,
|
||||||
|
// color: MihColors.getSecondaryColor(
|
||||||
|
// MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// const SizedBox(height: 10),
|
||||||
|
Text(
|
||||||
|
"Time Taken: ${_formatTime().replaceAll("00:", "")}",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
color: MihColors.getSecondaryColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Text(
|
||||||
|
"Score: ${calculateGameScore(mihMineSweeperProvider)}",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
color: MihColors.getSecondaryColor(
|
color: MihColors.getSecondaryColor(
|
||||||
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||||
),
|
),
|
||||||
@@ -347,8 +449,8 @@ class _MineSweeperGameState extends State<MineSweeperGame> {
|
|||||||
children: [
|
children: [
|
||||||
MihButton(
|
MihButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() => initializeBoard(mihMineSweeperProvider));
|
context.pop();
|
||||||
Navigator.of(context).pop();
|
showStartGameWindow(mihMineSweeperProvider, adProvider);
|
||||||
},
|
},
|
||||||
buttonColor: MihColors.getGreenColor(
|
buttonColor: MihColors.getGreenColor(
|
||||||
MzansiInnovationHub.of(context)!.theme.mode ==
|
MzansiInnovationHub.of(context)!.theme.mode ==
|
||||||
@@ -365,6 +467,26 @@ class _MineSweeperGameState extends State<MineSweeperGame> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
MihButton(
|
||||||
|
onPressed: () {
|
||||||
|
context.pop();
|
||||||
|
mihMineSweeperProvider.setToolIndex(1);
|
||||||
|
},
|
||||||
|
buttonColor: MihColors.getOrangeColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode ==
|
||||||
|
"Dark"),
|
||||||
|
width: 300,
|
||||||
|
child: Text(
|
||||||
|
"Leader Board",
|
||||||
|
style: TextStyle(
|
||||||
|
color: MihColors.getPrimaryColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode ==
|
||||||
|
"Dark"),
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -374,17 +496,35 @@ class _MineSweeperGameState extends State<MineSweeperGame> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return Mihloadingcircle(
|
||||||
|
message: "Uploading your score",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
await MihMinesweeperServices().addPlayerScore(
|
||||||
|
profileProvider,
|
||||||
|
mihMineSweeperProvider,
|
||||||
|
_formatTime().replaceAll("00:", ""),
|
||||||
|
calculateGameScore(mihMineSweeperProvider),
|
||||||
|
);
|
||||||
|
context.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Color? getDifficultyColor() {
|
Color? getDifficultyColor(MihMineSweeperProvider mihMineSweeperProvider) {
|
||||||
String mode = modeController.text;
|
String mode = mihMineSweeperProvider.difficulty;
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case "Easy":
|
case "Very Easy":
|
||||||
return MihColors.getGreenColor(
|
return MihColors.getGreenColor(
|
||||||
MzansiInnovationHub.of(context)!.theme.mode == "Dark",
|
MzansiInnovationHub.of(context)!.theme.mode == "Dark",
|
||||||
);
|
);
|
||||||
case "Normal":
|
case "Easy":
|
||||||
|
return MihColors.getGreenColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode != "Dark",
|
||||||
|
);
|
||||||
|
case "Intermediate":
|
||||||
return MihColors.getOrangeColor(
|
return MihColors.getOrangeColor(
|
||||||
MzansiInnovationHub.of(context)!.theme.mode == "Dark",
|
MzansiInnovationHub.of(context)!.theme.mode == "Dark",
|
||||||
);
|
);
|
||||||
@@ -398,42 +538,33 @@ class _MineSweeperGameState extends State<MineSweeperGame> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void dispose() {
|
||||||
super.initState();
|
_timer?.cancel();
|
||||||
modeController.text = "Easy";
|
super.dispose();
|
||||||
// showStartGameWindow(context.read<MihMineSweeperProvider>());
|
|
||||||
// initializeBoard();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didChangeDependencies() {
|
void initState() {
|
||||||
super.didChangeDependencies();
|
// UBongani was here during the MIH Live
|
||||||
// This method is safe for calling showDialog or reading provider values.
|
super.initState();
|
||||||
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Consumer<MihMineSweeperProvider>(
|
return MihPackageToolBody(
|
||||||
|
borderOn: false,
|
||||||
|
bodyItem: getBody(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget getBody() {
|
||||||
|
return Consumer3<MzansiProfileProvider, MihMineSweeperProvider,
|
||||||
|
MihBannerAdProvider>(
|
||||||
builder: (BuildContext context,
|
builder: (BuildContext context,
|
||||||
MihMineSweeperProvider mihMineSweeperProvider, Widget? child) {
|
MzansiProfileProvider profileProvider,
|
||||||
|
MihMineSweeperProvider mihMineSweeperProvider,
|
||||||
|
MihBannerAdProvider adProvider,
|
||||||
|
Widget? child) {
|
||||||
return Stack(
|
return Stack(
|
||||||
alignment: Alignment.topCenter,
|
alignment: Alignment.topCenter,
|
||||||
children: [
|
children: [
|
||||||
@@ -456,7 +587,7 @@ class _MineSweeperGameState extends State<MineSweeperGame> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Text(
|
Text(
|
||||||
"Welcom to MIH MineSweeper, the first game of MIH.",
|
"Welcom to Minesweeper, the first game of MIH.",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
overflow: TextOverflow.visible,
|
overflow: TextOverflow.visible,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@@ -495,7 +626,9 @@ class _MineSweeperGameState extends State<MineSweeperGame> {
|
|||||||
"Dark"),
|
"Dark"),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
TextSpan(text: " to start a new game."),
|
TextSpan(
|
||||||
|
text:
|
||||||
|
" to start a new game or learn how to play the minesweeper."),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -514,7 +647,8 @@ class _MineSweeperGameState extends State<MineSweeperGame> {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(10.0),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 10.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Mines: ${mihMineSweeperProvider.totalMines}',
|
'Mines: ${mihMineSweeperProvider.totalMines}',
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
@@ -526,20 +660,29 @@ class _MineSweeperGameState extends State<MineSweeperGame> {
|
|||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(10.0),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 10.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
modeController.text,
|
_formatTime().replaceAll("00:", ""),
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold),
|
||||||
color: getDifficultyColor(),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
Text(
|
||||||
|
mihMineSweeperProvider.difficulty,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: getDifficultyColor(mihMineSweeperProvider),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
// const SizedBox(
|
// const SizedBox(
|
||||||
// height: 30,
|
// height: 30,
|
||||||
// ),
|
// ),
|
||||||
@@ -571,13 +714,16 @@ class _MineSweeperGameState extends State<MineSweeperGame> {
|
|||||||
|
|
||||||
return MineTile(
|
return MineTile(
|
||||||
square: board[r][c],
|
square: board[r][c],
|
||||||
onTap: () =>
|
onTap: () => handleTap(profileProvider,
|
||||||
handleTap(mihMineSweeperProvider, r, c),
|
mihMineSweeperProvider, adProvider, r, c),
|
||||||
onLongPress: () => handleLongPress(r, c),
|
onLongPress: () => handleLongPress(r, c),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
SizedBox(height: 30),
|
||||||
|
MihBannerAd(),
|
||||||
|
// const SizedBox(height: 100),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -587,6 +733,30 @@ class _MineSweeperGameState extends State<MineSweeperGame> {
|
|||||||
child: MihFloatingMenu(
|
child: MihFloatingMenu(
|
||||||
animatedIcon: AnimatedIcons.menu_close,
|
animatedIcon: AnimatedIcons.menu_close,
|
||||||
children: [
|
children: [
|
||||||
|
SpeedDialChild(
|
||||||
|
child: Icon(
|
||||||
|
Icons.add,
|
||||||
|
color: MihColors.getPrimaryColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode ==
|
||||||
|
"Dark"),
|
||||||
|
),
|
||||||
|
label: "Learn how to play",
|
||||||
|
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: () {
|
||||||
|
mihMineSweeperProvider.setToolIndex(2);
|
||||||
|
},
|
||||||
|
),
|
||||||
SpeedDialChild(
|
SpeedDialChild(
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.add,
|
Icons.add,
|
||||||
@@ -610,9 +780,9 @@ class _MineSweeperGameState extends State<MineSweeperGame> {
|
|||||||
MzansiInnovationHub.of(context)!.theme.mode ==
|
MzansiInnovationHub.of(context)!.theme.mode ==
|
||||||
"Dark"),
|
"Dark"),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showStartGameWindow(mihMineSweeperProvider);
|
showStartGameWindow(mihMineSweeperProvider, adProvider);
|
||||||
},
|
},
|
||||||
)
|
),
|
||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -0,0 +1,279 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/main.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_tool_body.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_single_child_scroll.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
|
||||||
|
|
||||||
|
class MineSweeperQuickStartGuide extends StatefulWidget {
|
||||||
|
const MineSweeperQuickStartGuide({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MineSweeperQuickStartGuide> createState() =>
|
||||||
|
_MineSweeperQuickStartGuideState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MineSweeperQuickStartGuideState
|
||||||
|
extends State<MineSweeperQuickStartGuide> {
|
||||||
|
Widget _buildSectionTitle(String title) {
|
||||||
|
return Text(
|
||||||
|
title,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSubSectionTitle(String title) {
|
||||||
|
return Text(
|
||||||
|
title,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 17,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildRulePoint({
|
||||||
|
required String title,
|
||||||
|
required List<String> points,
|
||||||
|
required Color color,
|
||||||
|
}) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8.0, top: 4.0, bottom: 4.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
title,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: color,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
...points
|
||||||
|
.map((point) => Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 10.0),
|
||||||
|
child: Text(
|
||||||
|
'• $point',
|
||||||
|
style: const TextStyle(fontSize: 16),
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildNumberClue(String clue, String explanation) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8.0, bottom: 4.0),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
const Text('• ',
|
||||||
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
||||||
|
Expanded(
|
||||||
|
child: RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: MihColors.getYellowColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode != "Dark")),
|
||||||
|
children: <TextSpan>[
|
||||||
|
TextSpan(
|
||||||
|
text: 'If you see a $clue: ',
|
||||||
|
style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
TextSpan(text: explanation),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStrategyPoint(String title, String explanation,
|
||||||
|
{bool isAction = false}) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8.0, bottom: 4.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: MihColors.getSecondaryColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode == "Dark")),
|
||||||
|
children: <TextSpan>[
|
||||||
|
TextSpan(
|
||||||
|
text: title,
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: isAction ? FontWeight.bold : FontWeight.normal,
|
||||||
|
color: isAction
|
||||||
|
? MihColors.getRedColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode !=
|
||||||
|
"Dark")
|
||||||
|
: MihColors.getPurpleColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode !=
|
||||||
|
"Dark"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextSpan(text: isAction ? ' $explanation' : ': $explanation'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTipPoint(String title, String explanation) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8.0, bottom: 4.0),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Text('• ',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
color: MihColors.getOrangeColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode != "Dark"),
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
Expanded(
|
||||||
|
child: RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: MihColors.getSecondaryColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode == "Dark")),
|
||||||
|
children: <TextSpan>[
|
||||||
|
TextSpan(
|
||||||
|
text: title,
|
||||||
|
style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
TextSpan(text: explanation),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final Size size = MediaQuery.sizeOf(context);
|
||||||
|
final double width = size.width;
|
||||||
|
return MihPackageToolBody(
|
||||||
|
borderOn: false,
|
||||||
|
bodyItem: getBody(width),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget getBody(double width) {
|
||||||
|
return MihSingleChildScroll(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: width / 20),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Simple Rules and Strategy',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 30,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const Text(
|
||||||
|
'Minesweeper is a puzzle game where you use numbers to figure out where the hidden bombs (mines) are located.',
|
||||||
|
style: TextStyle(fontSize: 16),
|
||||||
|
),
|
||||||
|
const Divider(height: 30),
|
||||||
|
|
||||||
|
// --- 1. Two Main Actions ---
|
||||||
|
_buildSectionTitle('1. Two Main Actions (Your Controls)'),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
_buildRulePoint(
|
||||||
|
title: 'Quick Tap (or Click): This is the Dig action.',
|
||||||
|
points: [
|
||||||
|
'Goal: To uncover a square and see a number clue.',
|
||||||
|
'Risk: If you click a mine, the game ends!',
|
||||||
|
],
|
||||||
|
color: MihColors.getGreenColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode != "Dark"),
|
||||||
|
),
|
||||||
|
_buildRulePoint(
|
||||||
|
title:
|
||||||
|
'Tap and Hold (or Long Press): This is the Flag action (🚩).',
|
||||||
|
points: [
|
||||||
|
'Goal: To safely mark a square that you are **certain** is a mine.',
|
||||||
|
'Benefit: You cannot accidentally click a square that is flagged.',
|
||||||
|
],
|
||||||
|
color: MihColors.getRedColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode != "Dark"),
|
||||||
|
),
|
||||||
|
const Divider(height: 30),
|
||||||
|
|
||||||
|
// --- 2. The Golden Rule: Reading the Numbers ---
|
||||||
|
_buildSectionTitle('2. The Golden Rule: Reading the Numbers'),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const Text(
|
||||||
|
'The number tells you exactly how many mines are touching that square (including sides and corners).',
|
||||||
|
style: TextStyle(fontSize: 18, fontStyle: FontStyle.italic),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
_buildNumberClue('Blank Space (a \'0\')',
|
||||||
|
'Zero (0) mines are touching it. All surrounding squares are safe, and the game will open them for you automatically.'),
|
||||||
|
_buildNumberClue('\'1\'',
|
||||||
|
'Only **one** mine is touching this square. You must find and flag that single mine.'),
|
||||||
|
_buildNumberClue('\'3\'',
|
||||||
|
'Three mines are touching this square. You must find and flag all three.'),
|
||||||
|
const Divider(height: 30),
|
||||||
|
|
||||||
|
// --- 3. The Winning Strategy ---
|
||||||
|
_buildSectionTitle('3. The Winning Strategy (The Deduction Loop)'),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const Text(
|
||||||
|
'The game is won by uncovering every single safe square and correctly flagging all the mines. Use this two-step loop to clear the board:',
|
||||||
|
style: TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
|
||||||
|
_buildSubSectionTitle('A. Find the Mines (Where to Flag 🚩)'),
|
||||||
|
_buildStrategyPoint(
|
||||||
|
'Look for a number that only has one choice for a mine.',
|
||||||
|
'Example: If a \'1\' is touching only one hidden square, that hidden square **must** be the mine.'),
|
||||||
|
_buildStrategyPoint('Action:',
|
||||||
|
'Tap and Hold to place a **Flag** on the square you are sure is a mine.',
|
||||||
|
isAction: true),
|
||||||
|
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
|
||||||
|
_buildSubSectionTitle('B. Find the Safe Squares (Where to Dig)'),
|
||||||
|
_buildStrategyPoint(
|
||||||
|
'Look for a number that has been \'satisfied\' by your flags.',
|
||||||
|
'Example: You see a \'2\'. You have already placed two 🚩 flags touching it. The \'2\' is satisfied.'),
|
||||||
|
_buildStrategyPoint('Action:',
|
||||||
|
'Quick Tap any of the remaining hidden squares touching that \'satisfied\' number. They **must be safe** because the mine requirement has already been met.',
|
||||||
|
isAction: true),
|
||||||
|
|
||||||
|
const Divider(height: 30),
|
||||||
|
|
||||||
|
// --- Key Beginner Tip ---
|
||||||
|
_buildSectionTitle('✨ Key Beginner Tips'),
|
||||||
|
_buildTipPoint('Start on the Edges and Corners:',
|
||||||
|
'Numbers on the edge or corner of the board are easier to solve because they have fewer surrounding squares to check.'),
|
||||||
|
_buildTipPoint('Don\'t Guess:',
|
||||||
|
'If you are down to two squares and either one could be the mine, look somewhere else on the board for a guaranteed, safe move.'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:ken_logger/ken_logger.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/main.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_circle_avatar.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_icons.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_tool_body.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_loading_circle.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_components/mih_providers/mih_mine_sweeper_provider.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_components/mih_providers/mzansi_profile_provider.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_packages/mine_sweeper/builders/build_my_scoreboard_list.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_services/mih_minesweeper_services.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_services/mih_validation_services.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class MyScoreBoard extends StatefulWidget {
|
||||||
|
const MyScoreBoard({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MyScoreBoard> createState() => _MihMineSweeperLeaderBoardState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MihMineSweeperLeaderBoardState extends State<MyScoreBoard> {
|
||||||
|
TextEditingController filterController = TextEditingController();
|
||||||
|
|
||||||
|
Future<void> initialiseLeaderboard() async {
|
||||||
|
MzansiProfileProvider profileProvider =
|
||||||
|
context.read<MzansiProfileProvider>();
|
||||||
|
MihMineSweeperProvider mineSweeperProvider =
|
||||||
|
context.read<MihMineSweeperProvider>();
|
||||||
|
filterController.text = mineSweeperProvider.difficulty;
|
||||||
|
if (mineSweeperProvider.myScoreboard == null ||
|
||||||
|
mineSweeperProvider.myScoreboard!.isEmpty) {
|
||||||
|
KenLogger.success("getting data");
|
||||||
|
await MihMinesweeperServices()
|
||||||
|
.getMyScoreboard(profileProvider, mineSweeperProvider);
|
||||||
|
KenLogger.success("${mineSweeperProvider.myScoreboard}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void refreshLeaderBoard(
|
||||||
|
MihMineSweeperProvider mineSweeperProvider, String difficulty) {
|
||||||
|
mineSweeperProvider.setDifficulty(difficulty);
|
||||||
|
mineSweeperProvider.setLeaderboard(leaderboard: null);
|
||||||
|
mineSweeperProvider.setMyScoreboard(myScoreboard: null);
|
||||||
|
initialiseLeaderboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
|
await initialiseLeaderboard();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final double width = MediaQuery.sizeOf(context).width;
|
||||||
|
return Consumer<MihMineSweeperProvider>(
|
||||||
|
builder: (BuildContext context,
|
||||||
|
MihMineSweeperProvider mineSweeperProvider, Widget? child) {
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: () async {
|
||||||
|
refreshLeaderBoard(mineSweeperProvider, filterController.text);
|
||||||
|
},
|
||||||
|
child: MihPackageToolBody(
|
||||||
|
borderOn: false,
|
||||||
|
bodyItem: getBody(width),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget getBody(double width) {
|
||||||
|
return Consumer2<MzansiProfileProvider, MihMineSweeperProvider>(
|
||||||
|
builder: (BuildContext context, MzansiProfileProvider profileProvider,
|
||||||
|
MihMineSweeperProvider mineSweeperProvider, Widget? child) {
|
||||||
|
if (mineSweeperProvider.myScoreboard == null) {
|
||||||
|
return Center(
|
||||||
|
child: Mihloadingcircle(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return SingleChildScrollView(
|
||||||
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Center(
|
||||||
|
child: MihCircleAvatar(
|
||||||
|
imageFile: profileProvider.userProfilePicture,
|
||||||
|
width: 150,
|
||||||
|
editable: false,
|
||||||
|
fileNameController: null,
|
||||||
|
userSelectedfile: null,
|
||||||
|
frameColor: MihColors.getSecondaryColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||||
|
backgroundColor: MihColors.getPrimaryColor(
|
||||||
|
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
|
||||||
|
onChange: (selectedImage) {},
|
||||||
|
key: ValueKey(profileProvider.userProfilePicUrl),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: width / 20),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: MihDropdownField(
|
||||||
|
controller: filterController,
|
||||||
|
hintText: "Scoreboards",
|
||||||
|
dropdownOptions: const [
|
||||||
|
"Very Easy",
|
||||||
|
"Easy",
|
||||||
|
"Intermediate",
|
||||||
|
"Hard",
|
||||||
|
],
|
||||||
|
requiredText: true,
|
||||||
|
editable: true,
|
||||||
|
enableSearch: true,
|
||||||
|
validator: (value) {
|
||||||
|
return MihValidationServices().isEmpty(value);
|
||||||
|
},
|
||||||
|
onSelected: (selection) {
|
||||||
|
refreshLeaderBoard(mineSweeperProvider, selection!);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
mineSweeperProvider.myScoreboard!.isEmpty
|
||||||
|
? 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(
|
||||||
|
"You have played and ${mineSweeperProvider.difficulty} yet.",
|
||||||
|
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(
|
||||||
|
FontAwesomeIcons.bomb,
|
||||||
|
size: 20,
|
||||||
|
color: MihColors.getSecondaryColor(
|
||||||
|
MzansiInnovationHub.of(context)!
|
||||||
|
.theme
|
||||||
|
.mode ==
|
||||||
|
"Dark"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextSpan(text: " and start a new game"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: BuildMyScoreBoardList(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
80
Frontend/lib/mih_services/mih_minesweeper_services.dart
Normal file
80
Frontend/lib/mih_services/mih_minesweeper_services.dart
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_components/mih_objects/minesweeper_player_score.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_components/mih_providers/mih_mine_sweeper_provider.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_components/mih_providers/mzansi_profile_provider.dart';
|
||||||
|
import 'package:mzansi_innovation_hub/mih_config/mih_env.dart';
|
||||||
|
import 'package:supertokens_flutter/http.dart' as http;
|
||||||
|
|
||||||
|
class MihMinesweeperServices {
|
||||||
|
Future<int> getTop20Leaderboard(
|
||||||
|
MihMineSweeperProvider mineSweeperProvider,
|
||||||
|
) async {
|
||||||
|
String difficulty = mineSweeperProvider.difficulty;
|
||||||
|
var response = await http.get(
|
||||||
|
Uri.parse(
|
||||||
|
"${AppEnviroment.baseApiUrl}/minesweeper/leaderboard/top20/$difficulty"),
|
||||||
|
headers: <String, String>{
|
||||||
|
"Content-Type": "application/json; charset=UTF-8"
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
Iterable l = jsonDecode(response.body);
|
||||||
|
List<MinesweeperPlayerScore> leaderboard =
|
||||||
|
List<MinesweeperPlayerScore>.from(
|
||||||
|
l.map((model) => MinesweeperPlayerScore.fromJson(model)));
|
||||||
|
mineSweeperProvider.setLeaderboard(leaderboard: leaderboard);
|
||||||
|
} else {
|
||||||
|
mineSweeperProvider.setLeaderboard(leaderboard: null);
|
||||||
|
}
|
||||||
|
return response.statusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> getMyScoreboard(
|
||||||
|
MzansiProfileProvider profileProvider,
|
||||||
|
MihMineSweeperProvider mineSweeperProvider,
|
||||||
|
) async {
|
||||||
|
String difficulty = mineSweeperProvider.difficulty;
|
||||||
|
var response = await http.get(
|
||||||
|
Uri.parse(
|
||||||
|
"${AppEnviroment.baseApiUrl}/minesweeper/leaderboard/top_score/$difficulty/${profileProvider.user!.app_id}"),
|
||||||
|
headers: <String, String>{
|
||||||
|
"Content-Type": "application/json; charset=UTF-8"
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
Iterable l = jsonDecode(response.body);
|
||||||
|
List<MinesweeperPlayerScore> leaderboard =
|
||||||
|
List<MinesweeperPlayerScore>.from(
|
||||||
|
l.map((model) => MinesweeperPlayerScore.fromJson(model)));
|
||||||
|
mineSweeperProvider.setMyScoreboard(myScoreboard: leaderboard);
|
||||||
|
} else {
|
||||||
|
mineSweeperProvider.setMyScoreboard(myScoreboard: null);
|
||||||
|
}
|
||||||
|
return response.statusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> addPlayerScore(
|
||||||
|
MzansiProfileProvider profileProvider,
|
||||||
|
MihMineSweeperProvider mineSweeperProvider,
|
||||||
|
String game_time,
|
||||||
|
double game_score,
|
||||||
|
) async {
|
||||||
|
DateTime now = DateTime.now();
|
||||||
|
String formattedDateTime = now.toString();
|
||||||
|
var response = await http.post(
|
||||||
|
Uri.parse(
|
||||||
|
"${AppEnviroment.baseApiUrl}/minesweeper/leaderboard/player_score/insert/"),
|
||||||
|
headers: <String, String>{
|
||||||
|
"Content-Type": "application/json; charset=UTF-8"
|
||||||
|
},
|
||||||
|
body: jsonEncode(<String, dynamic>{
|
||||||
|
"app_id": profileProvider.user!.app_id,
|
||||||
|
"difficulty": mineSweeperProvider.difficulty,
|
||||||
|
"game_time": game_time,
|
||||||
|
"game_score": game_score,
|
||||||
|
"played_date": formattedDateTime,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
return response.statusCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
backend/.DS_Store
vendored
BIN
backend/.DS_Store
vendored
Binary file not shown.
@@ -20,6 +20,7 @@ import routers.mzansi_wallet as mzansi_wallet
|
|||||||
import routers.mzansi_directory as mzansi_directory
|
import routers.mzansi_directory as mzansi_directory
|
||||||
import routers.user_consent as user_consent
|
import routers.user_consent as user_consent
|
||||||
import routers.icd10_codes as icd10_codes
|
import routers.icd10_codes as icd10_codes
|
||||||
|
import routers.mine_sweeper_leaderboard as mine_sweeper_leaderboard
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from fastapi.middleware import Middleware
|
from fastapi.middleware import Middleware
|
||||||
from supertokens_python import get_all_cors_headers
|
from supertokens_python import get_all_cors_headers
|
||||||
@@ -98,6 +99,7 @@ app.include_router(mzansi_directory.router)
|
|||||||
app.include_router(user_consent.router)
|
app.include_router(user_consent.router)
|
||||||
app.include_router(icd10_codes.router)
|
app.include_router(icd10_codes.router)
|
||||||
app.include_router(appointments.router)
|
app.include_router(appointments.router)
|
||||||
|
app.include_router(mine_sweeper_leaderboard.router)
|
||||||
|
|
||||||
# Check if server is up
|
# Check if server is up
|
||||||
@app.get("/", tags=["Server Check"])
|
@app.get("/", tags=["Server Check"])
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from sqlalchemy import DateTime, Column, Integer, String, text
|
from sqlalchemy import DateTime, Column, Integer, String, DECIMAL, text
|
||||||
from sqlalchemy.orm import declarative_base
|
from sqlalchemy.orm import declarative_base
|
||||||
Base = declarative_base()
|
Base = declarative_base()
|
||||||
|
|
||||||
@@ -83,6 +83,7 @@ class BookmarkedBusiness(Base):
|
|||||||
f"business_id='{self.business_id}', created_date='{self.created_date}')>"
|
f"business_id='{self.business_id}', created_date='{self.created_date}')>"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class UserConsent(Base):
|
class UserConsent(Base):
|
||||||
__tablename__ = 'user_consent'
|
__tablename__ = 'user_consent'
|
||||||
__table_args__ = {'schema': 'app_data'}
|
__table_args__ = {'schema': 'app_data'}
|
||||||
@@ -98,3 +99,23 @@ class UserConsent(Base):
|
|||||||
f"privacy_policy_accepted='{self.privacy_policy_accepted}', "
|
f"privacy_policy_accepted='{self.privacy_policy_accepted}', "
|
||||||
f"terms_of_services_accepted='{self.terms_of_services_accepted}')>"
|
f"terms_of_services_accepted='{self.terms_of_services_accepted}')>"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class MineSweeperLeaderboard(Base):
|
||||||
|
__tablename__ = 'player_score'
|
||||||
|
__table_args__ = {'schema': 'minesweeper_leaderboard'}
|
||||||
|
idplayer_score = Column(Integer, primary_key=True)
|
||||||
|
app_id = Column(String(128), nullable=False,server_default=text("''"))
|
||||||
|
difficulty = Column(String(45), nullable=False,server_default=text("''"))
|
||||||
|
game_time = Column(String(45), nullable=False,server_default=text("''"))
|
||||||
|
game_score = Column(DECIMAL(45), nullable=False)
|
||||||
|
played_date = Column(DateTime, nullable=True)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return (
|
||||||
|
f"<MineSweeperLeaderboard(idplayer_score={self.idplayer_score}, "
|
||||||
|
f"app_id='{self.app_id}', "
|
||||||
|
f"difficulty='{self.difficulty}', "
|
||||||
|
f"game_time='{self.game_time}', "
|
||||||
|
f"game_score='{self.game_score}' "
|
||||||
|
f"played_date='{self.played_date}')>"
|
||||||
|
)
|
||||||
171
backend/routers/mine_sweeper_leaderboard.py
Normal file
171
backend/routers/mine_sweeper_leaderboard.py
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
from fastapi import APIRouter, HTTPException, status
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from supertokens_python.recipe.session.framework.fastapi import verify_session
|
||||||
|
from supertokens_python.recipe.session import SessionContainer
|
||||||
|
from fastapi import Depends
|
||||||
|
import mih_database
|
||||||
|
import mih_database.mihDbConnections
|
||||||
|
from mih_database.mihDbObjects import MineSweeperLeaderboard, User
|
||||||
|
from sqlalchemy import and_, func, literal_column
|
||||||
|
from sqlalchemy.orm import Session, aliased
|
||||||
|
from sqlalchemy.exc import IntegrityError, SQLAlchemyError
|
||||||
|
from datetime import datetime
|
||||||
|
from sqlalchemy.sql.expression import select
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
class playerScoreInsertRequest(BaseModel):
|
||||||
|
app_id: str
|
||||||
|
difficulty: str
|
||||||
|
game_time: str
|
||||||
|
game_score: float
|
||||||
|
played_date: datetime
|
||||||
|
|
||||||
|
# get top 20 scores
|
||||||
|
@router.get("/minesweeper/leaderboard/top20/{difficulty}", tags=["Minesweeper"])
|
||||||
|
async def get_user_consent(difficulty: str, session: SessionContainer = Depends(verify_session())):#session: SessionContainer = Depends(verify_session())
|
||||||
|
dbEngine = mih_database.mihDbConnections.dbAllConnect()
|
||||||
|
dbSession = Session(dbEngine)
|
||||||
|
try:
|
||||||
|
max_score_subquery = (
|
||||||
|
dbSession.query(
|
||||||
|
MineSweeperLeaderboard.app_id,
|
||||||
|
func.max(MineSweeperLeaderboard.game_score).label('max_score')
|
||||||
|
)
|
||||||
|
.filter(MineSweeperLeaderboard.difficulty == difficulty)
|
||||||
|
.group_by(MineSweeperLeaderboard.app_id)
|
||||||
|
.subquery('max_scores')
|
||||||
|
)
|
||||||
|
queryResults = (
|
||||||
|
dbSession.query(MineSweeperLeaderboard, User)
|
||||||
|
.join(User, User.app_id == MineSweeperLeaderboard.app_id)
|
||||||
|
.join(
|
||||||
|
max_score_subquery,
|
||||||
|
and_(
|
||||||
|
MineSweeperLeaderboard.app_id == max_score_subquery.c.app_id,
|
||||||
|
MineSweeperLeaderboard.game_score == max_score_subquery.c.max_score
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.filter(MineSweeperLeaderboard.difficulty == difficulty)
|
||||||
|
.order_by(MineSweeperLeaderboard.game_score.desc())
|
||||||
|
.limit(20)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
leaderboardData = []
|
||||||
|
if queryResults:
|
||||||
|
for playerScore, user in queryResults:
|
||||||
|
leaderboardData.append({
|
||||||
|
"app_id": playerScore.app_id,
|
||||||
|
"username": user.username,
|
||||||
|
"proPicUrl":user.pro_pic_path,
|
||||||
|
"difficulty":playerScore.difficulty,
|
||||||
|
"game_time":playerScore.game_time,
|
||||||
|
"game_score":playerScore.game_score,
|
||||||
|
"played_date":playerScore.played_date,
|
||||||
|
})
|
||||||
|
return leaderboardData
|
||||||
|
else:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail="No Score available for user."
|
||||||
|
)
|
||||||
|
except HTTPException as http_exc:
|
||||||
|
raise http_exc
|
||||||
|
except Exception as e:
|
||||||
|
print(f"An error occurred during the ORM query: {e}")
|
||||||
|
if dbSession.is_active:
|
||||||
|
dbSession.rollback()
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
detail="Failed to retrieve records due to an internal server error."
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
dbSession.close()
|
||||||
|
|
||||||
|
@router.get("/minesweeper/leaderboard/top_score/{difficulty}/{app_id}", tags=["Minesweeper"])
|
||||||
|
async def get_user_consent(app_id: str,
|
||||||
|
difficulty: str,
|
||||||
|
session: SessionContainer = Depends(verify_session())):#session: SessionContainer = Depends(verify_session())
|
||||||
|
dbEngine = mih_database.mihDbConnections.dbAllConnect()
|
||||||
|
dbSession = Session(dbEngine)
|
||||||
|
try:
|
||||||
|
queryResults =(dbSession.query(MineSweeperLeaderboard, User)
|
||||||
|
.join(User, User.app_id == MineSweeperLeaderboard.app_id)
|
||||||
|
.filter(
|
||||||
|
and_(
|
||||||
|
MineSweeperLeaderboard.app_id == app_id,
|
||||||
|
MineSweeperLeaderboard.difficulty == difficulty
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.order_by(MineSweeperLeaderboard.game_score.desc())
|
||||||
|
.all())
|
||||||
|
if not queryResults:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail="No scores found for this user and difficulty level."
|
||||||
|
)
|
||||||
|
leaderboard_data = []
|
||||||
|
for player_score, user in queryResults:
|
||||||
|
score_data = {
|
||||||
|
"app_id": player_score.app_id,
|
||||||
|
"username": user.username,
|
||||||
|
"proPicUrl": user.pro_pic_path,
|
||||||
|
"difficulty": player_score.difficulty,
|
||||||
|
"game_time": player_score.game_time,
|
||||||
|
"game_score": player_score.game_score,
|
||||||
|
"played_date": player_score.played_date,
|
||||||
|
}
|
||||||
|
leaderboard_data.append(score_data)
|
||||||
|
return leaderboard_data
|
||||||
|
except HTTPException as http_exc:
|
||||||
|
raise http_exc
|
||||||
|
except Exception as e:
|
||||||
|
print(f"An error occurred during the ORM query: {e}")
|
||||||
|
if dbSession.is_active:
|
||||||
|
dbSession.rollback()
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
detail="Failed to retrieve records due to an internal server error."
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
dbSession.close()
|
||||||
|
|
||||||
|
@router.post("/minesweeper/leaderboard/player_score/insert/",
|
||||||
|
tags=["Minesweeper"],
|
||||||
|
status_code=status.HTTP_201_CREATED)
|
||||||
|
async def insert_user_consent(itemRequest: playerScoreInsertRequest,
|
||||||
|
session: SessionContainer = Depends(verify_session())):#session: SessionContainer = Depends(verify_session())
|
||||||
|
dbEngine = mih_database.mihDbConnections.dbAllConnect()
|
||||||
|
dbSession = Session(dbEngine)
|
||||||
|
try:
|
||||||
|
newPlayerScore = MineSweeperLeaderboard(
|
||||||
|
app_id = itemRequest.app_id,
|
||||||
|
difficulty = itemRequest.difficulty,
|
||||||
|
game_time = itemRequest.game_time,
|
||||||
|
game_score = itemRequest.game_score,
|
||||||
|
played_date = itemRequest.played_date,
|
||||||
|
)
|
||||||
|
dbSession.add(newPlayerScore)
|
||||||
|
dbSession.commit()
|
||||||
|
dbSession.refresh(newPlayerScore)
|
||||||
|
return {"message": "Successfully Created Player Score Record"}
|
||||||
|
except IntegrityError as e:
|
||||||
|
dbSession.rollback()
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_409_CONFLICT, # 409 Conflict is often suitable for constraint errors
|
||||||
|
detail=f"Data integrity error: The provided data violates a database constraint. Details: {e.orig}"
|
||||||
|
) from e
|
||||||
|
except SQLAlchemyError as e:
|
||||||
|
dbSession.rollback()
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
detail=f"A database error occurred during insertion. Details: {e.orig}"
|
||||||
|
) from e
|
||||||
|
except Exception as e:
|
||||||
|
dbSession.rollback()
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
detail=f"An unexpected error occurred: {e}"
|
||||||
|
) from e
|
||||||
|
finally:
|
||||||
|
dbSession.close()
|
||||||
Reference in New Issue
Block a user