Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 15-improve-app-metadata
  • 24-add-fruits
  • 35-upgrade-framework-and-dependencies
  • master
  • Release_0.0.10_10
  • Release_0.0.11_11
  • Release_0.0.12_12
  • Release_0.0.1_1
  • Release_0.0.2_2
  • Release_0.0.3_3
  • Release_0.0.4_4
  • Release_0.0.5_5
  • Release_0.0.6_6
  • Release_0.0.7_7
  • Release_0.0.8_8
  • Release_0.0.9_9
  • Release_0.1.0_13
  • Release_0.1.1_14
  • Release_0.10.0_31
  • Release_0.2.0_15
  • Release_0.2.1_16
  • Release_0.3.0_17
  • Release_0.3.1_18
  • Release_0.4.0_19
  • Release_0.5.0_20
  • Release_0.5.1_21
  • Release_0.5.2_22
  • Release_0.5.3_23
  • Release_0.5.4_24
  • Release_0.6.0_25
  • Release_0.7.0_26
  • Release_0.8.0_27
  • Release_0.9.0_28
  • Release_0.9.1_29
  • Release_0.9.2_30
35 results

Target

Select target project
  • android/org.benoitharrault.snake
1 result
Select Git revision
  • 15-improve-app-metadata
  • 24-add-fruits
  • 35-upgrade-framework-and-dependencies
  • master
  • Release_0.0.10_10
  • Release_0.0.11_11
  • Release_0.0.12_12
  • Release_0.0.1_1
  • Release_0.0.2_2
  • Release_0.0.3_3
  • Release_0.0.4_4
  • Release_0.0.5_5
  • Release_0.0.6_6
  • Release_0.0.7_7
  • Release_0.0.8_8
  • Release_0.0.9_9
  • Release_0.1.0_13
  • Release_0.1.1_14
  • Release_0.10.0_31
  • Release_0.2.0_15
  • Release_0.2.1_16
  • Release_0.3.0_17
  • Release_0.3.1_18
  • Release_0.4.0_19
  • Release_0.5.0_20
  • Release_0.5.1_21
  • Release_0.5.2_22
  • Release_0.5.3_23
  • Release_0.5.4_24
  • Release_0.6.0_25
  • Release_0.7.0_26
  • Release_0.8.0_27
  • Release_0.9.0_28
  • Release_0.9.1_29
  • Release_0.9.2_30
35 results
Show changes
Showing
with 1045 additions and 306 deletions
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
class DefaultGameSettings {
// available game parameters codes
static const String parameterCodeLevel = 'level';
static const String parameterCodeSize = 'size';
static const List<String> availableParameters = [
parameterCodeLevel,
parameterCodeSize,
];
// level: available values
static const String levelValueEasy = 'easy';
static const String levelValueMedium = 'medium';
static const String levelValueHard = 'hard';
static const String levelValueNightmare = 'nightmare';
static const List<String> allowedLevelValues = [
levelValueEasy,
levelValueMedium,
levelValueHard,
levelValueNightmare,
];
// level: default value
static const String defaultLevelValue = levelValueMedium;
// size: available values
static const String sizeValueSmall = '10x10';
static const String sizeValueMedium = '15x15';
static const String sizeValueLarge = '20x20';
static const String sizeValueExtraLarge = '30x30';
static const List<String> allowedSizeValues = [
sizeValueSmall,
sizeValueMedium,
sizeValueLarge,
sizeValueExtraLarge,
];
// size: default value
static const String defaultSizeValue = sizeValueMedium;
// available values from parameter code
static List<String> getAvailableValues(String parameterCode) {
switch (parameterCode) {
case parameterCodeLevel:
return DefaultGameSettings.allowedLevelValues;
case parameterCodeSize:
return DefaultGameSettings.allowedSizeValues;
}
printlog('Did not find any available value for game parameter "$parameterCode".');
return [];
}
}
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
class DefaultGlobalSettings {
// available global parameters codes
static const String parameterCodeSkin = 'skin';
static const List<String> availableParameters = [
parameterCodeSkin,
];
// skin: available values
static const String skinValueColors = 'colors';
static const String skinValueRetro = 'retro';
static const List<String> allowedSkinValues = [
skinValueColors,
skinValueRetro,
];
// skin: default value
static const String defaultSkinValue = skinValueRetro;
// available values from parameter code
static List<String> getAvailableValues(String parameterCode) {
switch (parameterCode) {
case parameterCodeSkin:
return DefaultGlobalSettings.allowedSkinValues;
}
printlog('Did not find any available value for global parameter "$parameterCode".');
return [];
}
}
import 'package:flutter/material.dart';
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:snake/ui/screens/page_about.dart';
import 'package:snake/ui/screens/page_game.dart';
import 'package:snake/ui/screens/page_settings.dart';
class MenuItem {
final Icon icon;
final Widget page;
const MenuItem({
required this.icon,
required this.page,
});
}
class Menu {
static const indexGame = 0;
static const menuItemGame = MenuItem(
icon: Icon(UniconsLine.home),
page: PageGame(),
);
static const indexSettings = 1;
static const menuItemSettings = MenuItem(
icon: Icon(UniconsLine.setting),
page: PageSettings(),
);
static const indexAbout = 2;
static const menuItemAbout = MenuItem(
icon: Icon(UniconsLine.info_circle),
page: PageAbout(),
);
static Map<int, MenuItem> items = {
indexGame: menuItemGame,
indexSettings: menuItemSettings,
indexAbout: menuItemAbout,
};
static bool isIndexAllowed(int pageIndex) {
return items.keys.contains(pageIndex);
}
static Widget getPageWidget(int pageIndex) {
return items[pageIndex]?.page ?? menuItemGame.page;
}
static int itemsCount = Menu.items.length;
}
import 'package:flutter/material.dart';
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:snake/models/game/game.dart';
import 'package:snake/models/settings/settings_game.dart';
import 'package:snake/models/settings/settings_global.dart';
part 'game_state.dart';
class GameCubit extends HydratedCubit<GameState> {
GameCubit()
: super(GameState(
currentGame: Game.createEmpty(),
));
void updateState(Game game) {
emit(GameState(
currentGame: game,
));
}
void refresh() {
final Game game = Game(
// Settings
gameSettings: state.currentGame.gameSettings,
globalSettings: state.currentGame.globalSettings,
// State
isRunning: state.currentGame.isRunning,
isStarted: state.currentGame.isStarted,
isFinished: state.currentGame.isFinished,
animationInProgress: state.currentGame.animationInProgress,
// Base data
board: state.currentGame.board,
);
// game.dump();
updateState(game);
}
void startNewGame({
required GameSettings gameSettings,
required GlobalSettings globalSettings,
}) {
final Game newGame = Game.createNew(
// Settings
gameSettings: gameSettings,
globalSettings: globalSettings,
);
newGame.dump();
updateState(newGame);
refresh();
}
void quitGame() {
state.currentGame.isRunning = false;
refresh();
}
void resumeSavedGame() {
state.currentGame.isRunning = true;
refresh();
}
void deleteSavedGame() {
state.currentGame.isRunning = false;
state.currentGame.isFinished = true;
refresh();
}
@override
GameState? fromJson(Map<String, dynamic> json) {
final Game currentGame = json['currentGame'] as Game;
return GameState(
currentGame: currentGame,
);
}
@override
Map<String, dynamic>? toJson(GameState state) {
return <String, dynamic>{
'currentGame': state.currentGame.toJson(),
};
}
}
part of 'game_cubit.dart';
@immutable
class GameState extends Equatable {
const GameState({
required this.currentGame,
});
final Game currentGame;
@override
List<dynamic> get props => <dynamic>[
currentGame,
];
}
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:snake/config/menu.dart';
class NavCubit extends HydratedCubit<int> {
NavCubit() : super(0);
void updateIndex(int index) {
if (Menu.isIndexAllowed(index)) {
emit(index);
} else {
goToGamePage();
}
}
void goToGamePage() {
emit(Menu.indexGame);
}
void goToSettingsPage() {
emit(Menu.indexSettings);
}
void goToAboutPage() {
emit(Menu.indexAbout);
}
@override
int fromJson(Map<String, dynamic> json) {
return Menu.indexGame;
}
@override
Map<String, dynamic>? toJson(int state) {
return <String, int>{'pageIndex': state};
}
}
import 'package:flutter/material.dart';
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:snake/config/default_game_settings.dart';
import 'package:snake/models/settings/settings_game.dart';
part 'settings_game_state.dart';
class GameSettingsCubit extends HydratedCubit<GameSettingsState> {
GameSettingsCubit() : super(GameSettingsState(settings: GameSettings.createDefault()));
void setValues({
String? level,
String? size,
}) {
emit(
GameSettingsState(
settings: GameSettings(
level: level ?? state.settings.level,
size: size ?? state.settings.size,
),
),
);
}
String getParameterValue(String code) {
switch (code) {
case DefaultGameSettings.parameterCodeLevel:
return GameSettings.getLevelValueFromUnsafe(state.settings.level);
case DefaultGameSettings.parameterCodeSize:
return GameSettings.getSizeValueFromUnsafe(state.settings.size);
}
return '';
}
void setParameterValue(String code, String value) {
final String level = code == DefaultGameSettings.parameterCodeLevel
? value
: getParameterValue(DefaultGameSettings.parameterCodeLevel);
final String size = code == DefaultGameSettings.parameterCodeSize
? value
: getParameterValue(DefaultGameSettings.parameterCodeSize);
setValues(
level: level,
size: size,
);
}
@override
GameSettingsState? fromJson(Map<String, dynamic> json) {
final String level = json[DefaultGameSettings.parameterCodeLevel] as String;
final String size = json[DefaultGameSettings.parameterCodeSize] as String;
return GameSettingsState(
settings: GameSettings(
level: level,
size: size,
),
);
}
@override
Map<String, dynamic>? toJson(GameSettingsState state) {
return <String, dynamic>{
DefaultGameSettings.parameterCodeLevel: state.settings.level,
DefaultGameSettings.parameterCodeSize: state.settings.size,
};
}
}
part of 'settings_game_cubit.dart';
@immutable
class GameSettingsState extends Equatable {
const GameSettingsState({
required this.settings,
});
final GameSettings settings;
@override
List<dynamic> get props => <dynamic>[
settings,
];
}
import 'package:flutter/material.dart';
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:snake/config/default_global_settings.dart';
import 'package:snake/models/settings/settings_global.dart';
part 'settings_global_state.dart';
class GlobalSettingsCubit extends HydratedCubit<GlobalSettingsState> {
GlobalSettingsCubit() : super(GlobalSettingsState(settings: GlobalSettings.createDefault()));
void setValues({
String? skin,
}) {
emit(
GlobalSettingsState(
settings: GlobalSettings(
skin: skin ?? state.settings.skin,
),
),
);
}
String getParameterValue(String code) {
switch (code) {
case DefaultGlobalSettings.parameterCodeSkin:
return GlobalSettings.getSkinValueFromUnsafe(state.settings.skin);
}
return '';
}
void setParameterValue(String code, String value) {
final String skin = (code == DefaultGlobalSettings.parameterCodeSkin)
? value
: getParameterValue(DefaultGlobalSettings.parameterCodeSkin);
setValues(
skin: skin,
);
}
@override
GlobalSettingsState? fromJson(Map<String, dynamic> json) {
final String skin = json[DefaultGlobalSettings.parameterCodeSkin] as String;
return GlobalSettingsState(
settings: GlobalSettings(
skin: skin,
),
);
}
@override
Map<String, dynamic>? toJson(GlobalSettingsState state) {
return <String, dynamic>{
DefaultGlobalSettings.parameterCodeSkin: state.settings.skin,
};
}
}
part of 'settings_global_cubit.dart';
@immutable
class GlobalSettingsState extends Equatable {
const GlobalSettingsState({
required this.settings,
});
final GlobalSettings settings;
@override
List<dynamic> get props => <dynamic>[
settings,
];
}
import 'dart:math';
import 'package:flutter/material.dart';
import '../provider/data.dart';
import '../utils/game_utils.dart';
class Game {
static Container buildGameWidget(Data myProvider) {
bool gameIsFinished = myProvider.isGameFinished();
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Text('❇️'),
),
SizedBox(height: 2),
Container(
height: 150,
width: double.maxFinite,
child: gameIsFinished
? Game.buildEndGameMessage(myProvider)
: Text('❇️'),
),
],
),
);
}
static FlatButton buildRestartGameButton(Data myProvider) {
return FlatButton(
child: Container(
child: Image(
image: AssetImage('assets/icons/button_back.png'),
fit: BoxFit.fill,
),
),
onPressed: () => GameUtils.resetGame(myProvider),
);
}
static Container buildEndGameMessage(Data myProvider) {
String decorationImageAssetName = '';
if (myProvider.gameWon) {
decorationImageAssetName = 'assets/icons/game_win.png';
} else {
decorationImageAssetName = 'assets/icons/game_fail.png';
}
Image decorationImage = Image(
image: AssetImage(decorationImageAssetName),
fit: BoxFit.fill
);
return Container(
margin: EdgeInsets.all(2),
padding: EdgeInsets.all(2),
child: Table(
defaultColumnWidth: IntrinsicColumnWidth(),
children: [
TableRow(
children: [
Column(children: [ decorationImage ]),
Column(children: [ buildRestartGameButton(myProvider) ]),
Column(children: [ decorationImage ]),
],
),
]
)
);
}
}
import 'package:flutter/material.dart';
import '../provider/data.dart';
import '../utils/game_utils.dart';
class Parameters {
static Container buildParametersSelector(Data myProvider) {
return Container(
padding: EdgeInsets.all(2),
margin: EdgeInsets.all(2),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Parameters.buildParameterSelector(myProvider, 'level'),
SizedBox(height: 5),
Parameters.buildParameterSelector(myProvider, 'skin'),
SizedBox(height: 5),
Parameters.buildStartGameButton(myProvider),
],
),
);
}
static Container buildStartGameButton(Data myProvider) {
Column decorationImage = Column(
children: [
Image(
image: AssetImage('assets/icons/game_win.png'),
fit: BoxFit.fill
),
]
);
return Container(
margin: EdgeInsets.all(2),
padding: EdgeInsets.all(2),
child: Table(
defaultColumnWidth: IntrinsicColumnWidth(),
children: [
TableRow(
children: [
decorationImage,
Column(
children: [
FlatButton(
child: Container(
child: Image(
image: AssetImage('assets/icons/button_start.png'),
fit: BoxFit.fill,
),
),
onPressed: () => GameUtils.startGame(myProvider),
),
]
),
decorationImage,
],
),
]
)
);
}
static Widget buildParameterSelector(Data myProvider, String parameterCode) {
List availableValues = myProvider.getParameterAvailableValues(parameterCode);
if (availableValues.length == 1) {
return SizedBox(height: 1);
}
return Table(
defaultColumnWidth: IntrinsicColumnWidth(),
children: [
TableRow(
children: [
for (var index = 0; index < availableValues.length; index++)
Column(
children: [
_buildParameterButton(myProvider, parameterCode, availableValues[index])
]
),
],
),
],
);
}
static TextButton _buildParameterButton(Data myProvider, String parameterCode, String parameterValue) {
String currentValue = myProvider.getParameterValue(parameterCode).toString();
bool isActive = (parameterValue == currentValue);
String imageAsset = 'assets/icons/' + parameterCode + '_' + parameterValue + '.png';
return TextButton(
child: Container(
padding: EdgeInsets.all(2),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: isActive ? Colors.blue : Colors.white,
width: 10,
),
),
child: Image(
image: AssetImage(imageAsset),
fit: BoxFit.fill,
),
),
onPressed: () => myProvider.setParameterValue(parameterCode, parameterValue),
);
}
}
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:overlay_support/overlay_support.dart';
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'provider/data.dart';
import 'screens/home.dart';
import 'package:snake/config/default_global_settings.dart';
import 'package:snake/cubit/game_cubit.dart';
import 'package:snake/cubit/nav_cubit.dart';
import 'package:snake/cubit/settings_game_cubit.dart';
import 'package:snake/cubit/settings_global_cubit.dart';
import 'package:snake/ui/skeleton.dart';
void main() {
void main() async {
// Initialize packages
WidgetsFlutterBinding.ensureInitialized();
await EasyLocalization.ensureInitialized();
final Directory tmpDir = await getTemporaryDirectory();
Hive.init(tmpDir.toString());
HydratedBloc.storage = await HydratedStorage.build(
storageDirectory: tmpDir,
);
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
.then((value) => runApp(MyApp()));
.then((value) => runApp(EasyLocalization(
path: 'assets/translations',
supportedLocales: const <Locale>[
Locale('en'),
Locale('fr'),
],
fallbackLocale: const Locale('en'),
useFallbackTranslations: true,
child: const MyApp(),
)));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (BuildContext context) => Data(),
child: Consumer<Data>(builder: (context, data, child) {
return OverlaySupport(
child: MaterialApp(
final List<String> assets = getImagesAssets();
for (String asset in assets) {
precacheImage(AssetImage(asset), context);
}
return MultiBlocProvider(
providers: [
BlocProvider<NavCubit>(create: (context) => NavCubit()),
BlocProvider<ApplicationThemeModeCubit>(
create: (context) => ApplicationThemeModeCubit()),
BlocProvider<GameCubit>(create: (context) => GameCubit()),
BlocProvider<GlobalSettingsCubit>(create: (context) => GlobalSettingsCubit()),
BlocProvider<GameSettingsCubit>(create: (context) => GameSettingsCubit()),
],
child: BlocBuilder<ApplicationThemeModeCubit, ApplicationThemeModeState>(
builder: (BuildContext context, ApplicationThemeModeState state) {
return MaterialApp(
title: 'Snake',
home: const SkeletonScreen(),
// Theme stuff
theme: lightTheme,
darkTheme: darkTheme,
themeMode: state.themeMode,
// Localization stuff
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Home(),
routes: {
Home.id: (context) => Home(),
);
},
));
}),
),
);
}
List<String> getImagesAssets() {
final List<String> assets = [];
const List<String> gameImages = [
'button_back',
'button_delete_saved_game',
'button_resume_game',
'button_start',
'game_fail',
'game_win',
'placeholder',
];
for (String image in gameImages) {
assets.add('assets/ui/$image.png');
}
final List<String> skinImages = [
'body',
'empty',
'fruit',
'head',
];
for (String skin in DefaultGlobalSettings.allowedSkinValues) {
for (String image in skinImages) {
assets.add('assets/skins/${skin}_$image.png');
}
}
return assets;
}
}
import 'dart:math';
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:snake/models/game/cell.dart';
import 'package:snake/models/game/cell_location.dart';
typedef BoardCells = List<List<Cell>>;
class Board {
Board({
required this.cells,
});
BoardCells cells = const [];
factory Board.createEmpty() {
return Board(
cells: [],
);
}
factory Board.createNew({
required BoardCells cells,
}) {
return Board(
cells: cells,
);
}
Cell get(CellLocation location) {
if (location.row < cells.length) {
if (location.col < cells[location.row].length) {
return cells[location.row][location.col];
}
}
return Cell.none;
}
void set(CellLocation location, Cell cell) {
cells[location.row][location.col] = cell;
}
BoardCells copyCells() {
final BoardCells copiedGrid = [];
for (int rowIndex = 0; rowIndex < cells.length; rowIndex++) {
final List<Cell> row = [];
for (int colIndex = 0; colIndex < cells[rowIndex].length; colIndex++) {
row.add(Cell(
location: CellLocation.go(rowIndex, colIndex),
value: cells[rowIndex][colIndex].value,
isFixed: false,
));
}
copiedGrid.add(row);
}
return copiedGrid;
}
BoardCells getSolvedGrid() {
final Board tmpBoard = Board(cells: copyCells());
do {
final List<List<int>> cellsWithUniqueAvailableValue =
tmpBoard.getEmptyCellsWithUniqueAvailableValue();
if (cellsWithUniqueAvailableValue.isEmpty) {
break;
}
for (int i = 0; i < cellsWithUniqueAvailableValue.length; i++) {
final int row = cellsWithUniqueAvailableValue[i][0];
final int col = cellsWithUniqueAvailableValue[i][1];
final int value = cellsWithUniqueAvailableValue[i][2];
tmpBoard.cells[row][col] = Cell(
location: CellLocation.go(row, col),
value: value,
isFixed: tmpBoard.cells[row][col].isFixed,
);
}
} while (true);
return tmpBoard.cells;
}
List<List<int>> getEmptyCellsWithUniqueAvailableValue() {
List<List<int>> candidateCells = [];
final int boardSize = cells.length;
for (int row = 0; row < boardSize; row++) {
for (int col = 0; col < boardSize; col++) {
if (cells[row][col].value == 0) {
int allowedValuesCount = 0;
int candidateValue = 0;
for (int value = 1; value <= boardSize; value++) {
if (isValueAllowed(CellLocation.go(row, col), value)) {
candidateValue = value;
allowedValuesCount++;
}
}
if (allowedValuesCount == 1) {
candidateCells.add([row, col, candidateValue]);
}
}
}
}
return candidateCells;
}
bool isValueAllowed(CellLocation? candidateLocation, int candidateValue) {
if ((candidateLocation == null) || (candidateValue == 0)) {
return true;
}
final int boardSize = cells.length;
// check lines does not contains a value twice
for (int row = 0; row < boardSize; row++) {
final List<int> values = [];
for (int col = 0; col < boardSize; col++) {
int value = cells[row][col].value;
if (row == candidateLocation.row && col == candidateLocation.col) {
value = candidateValue;
}
if (value != 0) {
values.add(value);
}
}
final List<int> distinctValues = values.toSet().toList();
if (values.length != distinctValues.length) {
return false;
}
}
// check columns does not contains a value twice
for (int col = 0; col < boardSize; col++) {
final List<int> values = [];
for (int row = 0; row < boardSize; row++) {
int value = cells[row][col].value;
if (row == candidateLocation.row && col == candidateLocation.col) {
value = candidateValue;
}
if (value != 0) {
values.add(value);
}
}
final List<int> distinctValues = values.toSet().toList();
if (values.length != distinctValues.length) {
return false;
}
}
// check blocks does not contains a value twice
final int blockSizeVertical = sqrt(cells.length).toInt();
final int blockSizeHorizontal = cells.length ~/ blockSizeVertical;
final int horizontalBlocksCount = blockSizeVertical;
final int verticalBlocksCount = blockSizeHorizontal;
for (int blockRow = 0; blockRow < verticalBlocksCount; blockRow++) {
for (int blockCol = 0; blockCol < horizontalBlocksCount; blockCol++) {
final List<int> values = [];
for (int rowInBlock = 0; rowInBlock < blockSizeVertical; rowInBlock++) {
for (int colInBlock = 0; colInBlock < blockSizeHorizontal; colInBlock++) {
final int row = (blockRow * blockSizeVertical) + rowInBlock;
final int col = (blockCol * blockSizeHorizontal) + colInBlock;
int value = cells[row][col].value;
if (row == candidateLocation.row && col == candidateLocation.col) {
value = candidateValue;
}
if (value != 0) {
values.add(value);
}
}
}
final List<int> distinctValues = values.toSet().toList();
if (values.length != distinctValues.length) {
return false;
}
}
}
return true;
}
void dump() {
printlog('');
printlog('$Board:');
printlog(' cells: $cells');
printlog('');
}
@override
String toString() {
return '$Board(${toJson()})';
}
Map<String, dynamic>? toJson() {
return <String, dynamic>{
'cells': cells,
};
}
}
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:snake/models/game/cell_location.dart';
class Cell {
const Cell({
required this.location,
required this.value,
required this.isFixed,
});
final CellLocation location;
final int value;
final bool isFixed;
static Cell none = Cell(
location: CellLocation.go(0, 0),
value: 0,
isFixed: true,
);
void dump() {
printlog('$Cell:');
printlog(' location: $location');
printlog(' value: $value');
printlog(' isFixed: $isFixed');
printlog('');
}
@override
String toString() {
return '$Cell(${toJson()})';
}
Map<String, dynamic>? toJson() {
return <String, dynamic>{
'location': location.toJson(),
'value': value,
'isFixed': isFixed,
};
}
}
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
class CellLocation {
final int col;
final int row;
CellLocation({
required this.col,
required this.row,
});
factory CellLocation.go(int row, int col) {
return CellLocation(col: col, row: row);
}
void dump() {
printlog('$CellLocation:');
printlog(' row: $row');
printlog(' col: $col');
printlog('');
}
@override
String toString() {
return '$CellLocation(${toJson()})';
}
Map<String, dynamic>? toJson() {
return <String, dynamic>{
'row': row,
'col': col,
};
}
}
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:snake/models/game/board.dart';
import 'package:snake/models/settings/settings_game.dart';
import 'package:snake/models/settings/settings_global.dart';
class Game {
Game({
// Settings
required this.gameSettings,
required this.globalSettings,
// State
this.isRunning = false,
this.isStarted = false,
this.isFinished = false,
this.animationInProgress = false,
// Base data
required this.board,
// Game data
this.score = 0,
this.gameWon = false,
});
// Settings
final GameSettings gameSettings;
final GlobalSettings globalSettings;
// State
bool isRunning;
bool isStarted;
bool isFinished;
bool animationInProgress;
// Base data
final Board board;
// Game data
int score;
bool gameWon;
factory Game.createEmpty() {
return Game(
// Settings
gameSettings: GameSettings.createDefault(),
globalSettings: GlobalSettings.createDefault(),
// Base data
board: Board.createEmpty(),
);
}
factory Game.createNew({
GameSettings? gameSettings,
GlobalSettings? globalSettings,
}) {
final GameSettings newGameSettings = gameSettings ?? GameSettings.createDefault();
final GlobalSettings newGlobalSettings = globalSettings ?? GlobalSettings.createDefault();
final Board board = Board.createEmpty();
return Game(
// Settings
gameSettings: newGameSettings,
globalSettings: newGlobalSettings,
// State
isRunning: true,
// Base data
board: board,
);
}
bool get canBeResumed => isStarted && !isFinished;
void dump() {
printlog('');
printlog('## Current game dump:');
printlog('');
printlog('$Game:');
printlog(' Settings');
gameSettings.dump();
globalSettings.dump();
printlog(' State');
printlog(' isRunning: $isRunning');
printlog(' isStarted: $isStarted');
printlog(' isFinished: $isFinished');
printlog(' animationInProgress: $animationInProgress');
printlog(' Base data');
printlog('board:');
board.dump();
printGrid();
printlog(' Game data');
printlog(' score: $score');
printlog('');
}
printGrid() {
final BoardCells cells = board.cells;
const String stringValues = '0123456789ABCDEFG';
printlog('');
printlog('-------');
for (int rowIndex = 0; rowIndex < cells.length; rowIndex++) {
String row = '';
for (int colIndex = 0; colIndex < cells[rowIndex].length; colIndex++) {
row += stringValues[cells[rowIndex][colIndex].value];
}
printlog(row);
}
printlog('-------');
printlog('');
}
@override
String toString() {
return '$Game(${toJson()})';
}
Map<String, dynamic>? toJson() {
return <String, dynamic>{
// Settings
'gameSettings': gameSettings.toJson(),
'globalSettings': globalSettings.toJson(),
// State
'isRunning': isRunning,
'isStarted': isStarted,
'isFinished': isFinished,
'animationInProgress': animationInProgress,
// Base data
'board': board.toJson(),
// Game data
'score': score,
};
}
}
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:snake/config/default_game_settings.dart';
class GameSettings {
final String level;
final String size;
GameSettings({
required this.level,
required this.size,
});
static String getLevelValueFromUnsafe(String level) {
if (DefaultGameSettings.allowedLevelValues.contains(level)) {
return level;
}
return DefaultGameSettings.defaultLevelValue;
}
static String getSizeValueFromUnsafe(String size) {
if (DefaultGameSettings.allowedSizeValues.contains(size)) {
return size;
}
return DefaultGameSettings.defaultSizeValue;
}
factory GameSettings.createDefault() {
return GameSettings(
level: DefaultGameSettings.defaultLevelValue,
size: DefaultGameSettings.defaultSizeValue,
);
}
void dump() {
printlog('$GameSettings:');
printlog(' ${DefaultGameSettings.parameterCodeLevel}: $level');
printlog(' ${DefaultGameSettings.parameterCodeSize}: $size');
printlog('');
}
@override
String toString() {
return '$GameSettings(${toJson()})';
}
Map<String, dynamic>? toJson() {
return <String, dynamic>{
DefaultGameSettings.parameterCodeLevel: level,
DefaultGameSettings.parameterCodeSize: size,
};
}
}
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:snake/config/default_global_settings.dart';
class GlobalSettings {
String skin;
GlobalSettings({
required this.skin,
});
static String getSkinValueFromUnsafe(String skin) {
if (DefaultGlobalSettings.allowedSkinValues.contains(skin)) {
return skin;
}
return DefaultGlobalSettings.defaultSkinValue;
}
factory GlobalSettings.createDefault() {
return GlobalSettings(
skin: DefaultGlobalSettings.defaultSkinValue,
);
}
void dump() {
printlog('$GlobalSettings:');
printlog(' ${DefaultGlobalSettings.parameterCodeSkin}: $skin');
printlog('');
}
@override
String toString() {
return '$GlobalSettings(${toJson()})';
}
Map<String, dynamic>? toJson() {
return <String, dynamic>{
DefaultGlobalSettings.parameterCodeSkin: skin,
};
}
}
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
class Data extends ChangeNotifier {
// Configuration available values
List _availableLevelValues = ['easy', 'normal', 'hard', 'nightmare'];
List _availableSkinValues = ['colors', 'retro'];
List get availableLevelValues => _availableLevelValues;
List get availableSkinValues => _availableSkinValues;
// Application default configuration
String _level = '';
String _levelDefault = 'normal';
String _skin = '';
String _skinDefault = 'colors';
// Game data
bool _gameIsRunning = false;
bool _gameWon = false;
String get level => _level;
void updateLevel(String level) {
_level = level;
notifyListeners();
}
String get skin => _skin;
void updateSkin(String skin) {
_skin = skin;
notifyListeners();
}
getParameterValue(String parameterCode) {
switch(parameterCode) {
case 'level': { return _level; }
break;
case 'skin': { return _skin; }
break;
}
}
List getParameterAvailableValues(String parameterCode) {
switch(parameterCode) {
case 'level': { return _availableLevelValues; }
break;
case 'skin': { return _availableSkinValues; }
break;
}
return [];
}
setParameterValue(String parameterCode, String parameterValue) async {
switch(parameterCode) {
case 'level': { updateLevel(parameterValue); }
break;
case 'skin': { updateSkin(parameterValue); }
break;
}
final prefs = await SharedPreferences.getInstance();
prefs.setString(parameterCode, parameterValue);
}
void initParametersValues() async {
final prefs = await SharedPreferences.getInstance();
setParameterValue('level', prefs.getString('level') ?? _levelDefault);
setParameterValue('skin', prefs.getString('skin') ?? _skinDefault);
}
bool get gameIsRunning => _gameIsRunning;
void updateGameIsRunning(bool gameIsRunning) {
_gameIsRunning = gameIsRunning;
notifyListeners();
}
bool isGameFinished() {
return false;
}
bool get gameWon => _gameWon;
void resetGame() {
_gameIsRunning = false;
_gameWon = false;
notifyListeners();
}
}